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?
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;
}
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;
}