Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auto-interpreting a c-style string as a std::string via Boost's Property tree's .get function

I use boosts property tree, included via

#include "boost\property_tree\ptree.hpp"

And... I'd like to create a simple function which substitutes a value in case none is found via a fairly straight-forward template function:

template <typename Type>
Type getValueOrDefault( std::string const& str, Type defaultValue )
{
    Type returnValue = defaultValue;

    try {
        returnValue = mSettings.get<Type>( str );
    }
    catch ( boost::property_tree::ptree_error &e )
    {
        // Log error!
    }

    return returnValue;
}

This works well in principle, but runs into a bit problems if I rely on C-style string. For example, calling the function as follows:

getValueOrDefault( "pathToImportantStuffParameter", "c:/defaultdir/" )

will result in the following error:

boost\property_tree\stream_translator.hpp(36): error C2678: binary '>>' : no operator found which takes a left-hand operand of type 'std::basic_istream<char,std::char_traits<char>>' (or there is no acceptable conversion)

The error stems from passing char const * as a template parameter which makes a fair bit of sense. Two obvious solutions to this issue would be to force the default value to be a std::string object, like so:

getValueOrDefault<std::string>( "pathToImportantStuffParameter", "c:/defaultdir/" )
getValueOrDefault( "pathToImportantStuffParameter", std::string("c:/defaultdir/") )

But I'm wondering if someone might know of some template magic I could sprinkle to automatically interpret c-style strings as std::strings?

like image 830
Gazoo Avatar asked Apr 12 '26 16:04

Gazoo


2 Answers

You can provide a char array overload which converts the char array to a std::string and then calls the default implementation:

#include <iostream>
#include <string>

template <typename T>
T getValueOrDefault(const std::string& str, T&& defaultValue)
{
    std::cout << "inside default implementation" << std::endl;
    /* ... */
    return defaultValue;
}

template <std::size_t N>
std::string getValueOrDefault(const std::string& str, const char (&defaultValue)[N])
{
    std::cout << "inside char[] overload" << std::endl;
    return getValueOrDefault(str, std::string(defaultValue));
}


int main()
{
    auto x = getValueOrDefault("foo", "bar");
    return 0;
}

live example


An alternative solution is to use custom type traits:

#include <string>
#include <type_traits>

template <typename T>
struct return_type
{
    using type = T;
};

template <>
struct return_type<const char*>
{
    using type = std::string;
};

template <typename T>
using return_type_t = typename return_type<typename std::decay<T>::type>::type;

template <typename T>
return_type_t<T> getValueOrDefault(const std::string& str, T&& defaultValue)
{
    return_type_t<T> value(defaultValue);
    /* ... */
    return value;
}


int main()
{
    auto x = getValueOrDefault("foo", "bar");
    static_assert(std::is_same<decltype(x), std::string>::value, "");
    return 0;
}

live example

like image 155
m.s. Avatar answered Apr 15 '26 07:04

m.s.


The only way I found is to specialize getValueOrDefault for const char*, which calls getValueOrDefault with std::string explicitly:

//Note that the return value is unspecified, it returns a 'const char*' to a temporary,
//which will be destroyed when the function returns
template <>
const char* getValueOrDefault(std::string const& str, const char* defaultValue)
{
    return getValueOrDefault<std::string>(str, defaultValue).c_str();
}

If you want to that function to return a std::string instead of an invalid const char*, you have to change the template signature a bit:

//Default return type is the same as paramter
template <typename Type, typename Return = Type>
Return getValueOrDefault(std::string const& str, Type defaultValue)
{
    //...
}

//Trick the compiler to select this overload for 'const char*'
template <typename Return = std::string>
Return getValueOrDefault(std::string const& str, const char* defaultValue)
{
    return getValueOrDefault<std::string, std::string>(str, defaultValue);
}

or you could just plain overload the function (thanks @m.s.)

//Overload for 'const char*'
std::string getValueOrDefault(std::string const& str, const char* defaultValue)
{
    return getValueOrDefault<std::string>(str, defaultValue);
}

There is also a third way (if you can use C++14), using the string literal ""s:

//"c:/defaultdir/"s is a std::string (note the s after it => string literal)
getValueOrDefault("pathToImportantStuffParameter", "c:/defaultdir/"s);
like image 24
Rakete1111 Avatar answered Apr 15 '26 06:04

Rakete1111



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!