Template to match custom functors or stod, stoi, etc - c++

I'm trying to store key-value parameters as string in a class named ModelConfig. Then I would like to automatically convert these values into specific types, either with custom conversion function or with standard functions stod, stof, stoi, and the like.
My class successfully parses parameters if I provide a custom conversion function, but I can't figure how to also accept standard functions. This is my approach:
class ModelConfig
{
public:
ModelConfig(void) = default;
void addParam(std::string pname, std::string pvalue) {
m_params[pname] = pvalue;
}
template <class F, typename... Args, class T = typename std::result_of<F&&(const std::string&, Args...)>::type>
T getParam(std::string pname, F&& pconv_functor) const
{
return pconv_functor(m_params.at(pname));
}
private:
std::map<std::string, std::string> m_params;
};
The class above, can be tested with:
#include <iostream>
#include <map>
#include <functional>
#include "ModelConfig.hpp"
int main(void)
{
ModelConfig mc;
mc.addParam("p1_float", "123.4");
mc.addParam("p2_double", "56.7");
mc.addParam("p3_bool", "true");
mc.addParam("p4_int", "-321");
auto functord = [](const std::string& s) {
return std::stod(s);
};
std::cout << mc.getParam("p2_double", functord) << "\n"; // OK.
std::cout << mc.getParam("p2_double", std::stod) << "\n"; // Error.
return 0;
}
How can I modify getParam to accept functions where their first argument is a string but which can have others with default values?

std::stod is overloaded, thus the compiler can't deduce which function to use.
You can use macro to write a generic wrapper:
#define wrapper(f) \
( [] (auto&&... args) -> decltype(auto) { \
return f(std::forward<decltype(args)>(args)...); \
} )
Then call it by:
std::cout << mc.getParam("p2_double", wrapper(std::stod)) << "\n";

An alternative and, IMO, better design is to store values as std/boost::variant<bool, long, double, std::string> and convert it to/from string during I/O. This also detects config file errors early on load, rather than on first value access which could happen much later and crash your application in front of the user.
Requiring the user of this API to always pass a conversion function is cumbersome. You can use boost::lexical_cast for converting strings to T:
#include <string>
#include <iostream>
#include <unordered_map>
#include <boost/lexical_cast.hpp>
struct ConvertProxy {
std::string const* value_;
template<class T>
T as() const {
return boost::lexical_cast<T>(*value_);
}
template<class T>
operator T() const {
return this->as<T>();
}
};
class ModelConfig {
std::unordered_map<std::string, std::string> m_params;
public:
void addParam(std::string pname, std::string pvalue) {
m_params[pname] = pvalue;
}
ConvertProxy getParam(std::string pname) const {
return {&m_params.at(pname)};
}
};
int main() {
ModelConfig mc;
mc.addParam("p1_float", "123.4");
mc.addParam("p2_double", "56.7");
mc.addParam("p3_bool", "true");
mc.addParam("p4_int", "-321");
// Example syntax.
double d1 = mc.getParam("p2_double");
auto d2 = mc.getParam("p2_double").as<double>();
auto d3 = static_cast<double>(mc.getParam("p2_double"));
std::cout << mc.getParam("p2_double").as<double>() << "\n";
std::cout << static_cast<double>(mc.getParam("p2_double")) << "\n";
}
The interface of boost::lexical_cast enables an easy solution here. If you cannot use boost::lexical_cast you should probably code up your own with a similar interface.

You can do this with no third party lib and without using preprocessor directives if you need:
by explicitely casting your standard functions pointers. Standard functions are overloaded for string and wstring so the compiler needs our help to determine which one to apply
and by slightly changing your functor's signature to adapt it to the signature of these standard functions as they have a second parameter.
These changes would be slight actually:
In ModelConfig:
class ModelConfig
{
[...]
// Adapted the functor's signature to comply to standard functions' signatures:
template <class F, typename... Args, class T = typename std::result_of<F && (const std::string&, size_t *)>::type>
T getParam(std::string pname, F&& pconv_functor) const
{
return pconv_functor(m_params.at(pname), 0);
}
[...]
};
In main():
int main(void)
{
[...]
// Adapted the functor to standard functions' signature
auto functord = [](const std::string& s, size_t * pos) {
return std::stod(s, pos);
};
// Unchanged, no need
std::cout << mc.getParam("p2_double", functord) << "\n"; // Still OK.
// Cast to determine which overload to use. The typedef helps having things readable.
typedef double(*StandardFunctionSignature)(const std::string&, size_t*);
std::cout << mc.getParam("p2_double", static_cast<StandardFunctionSignature>(std::stod)) << "\n"; // NO Error, it works now.
[...]
}

If you know the signature of the passed in overload set, you can make an additional overload that captures a specific function pointer from that set.
template <class F>
auto getParam(std::string pname, F&& pconv_functor) const
{
return pconv_functor(m_params.at(pname));
}
template <class F>
auto getParam(std::string pname, F(*pconv_functor)(const std::string&, std::size_t*)) const
{
return pconv_functor(m_params.at(pname), 0);
}
This has some obvious limitations, but can be useful in certain situations.

Related

casting a function back to the original signature using variadic args

I've found this interesting code here on stackoverflow from:
Using a STL map of function pointers
template<typename T,typename... Args>
T searchAndCall(std::string s1, Args&&... args){
// ....
// auto typeCastedFun = reinterpret_cast<T(*)(Args ...)>(mapVal.first);
auto typeCastedFun = (T(*)(Args ...))(mapVal.first);
//compare the types is equal or not
assert(mapVal.second == std::type_index(typeid(typeCastedFun)));
return typeCastedFun(std::forward<Args>(args)...);
}
};
Basically, mapVal is a map of function pointers casted to void(*)(void) that will be casted back to their original type with this function. What I would like to do know is how typeCastedFun will be deduced when you don't specify the template parameters.
For instance, let's suppose that you had:
int f(const MyClass& a, MyClass b) {...}
... if you have:
MyClass first, second;
searchAndCall<int>(first, second);
What Args... parameter will be deduced? if I recall correctly, using the function casted back to a function with a different signature compared to the original one, should yield undefined behavior. Is there any other alternative?
What I would like to do is a way to store the type of the function somewhere and use this information to do the correct cast. Everything in the most efficient way.
Thanks
[edit1]
More specifically, I'm trying to build a kind of generic function dispatcher, able to call functions (templated with an enum class value) with different signatures using a lookup table for efficiency reasons. No boost::any as it internally uses a new
[edit2] Use of macros is not allowed
The key problem is that by taking the calling argument types directly, and attempting to cast the function pointer, you are losing all implicit conversions.
Your function signature has to match exactly, or you will get UB if you try to call it. And there is generally no way to get the signature from the args without manually specifying it at the call site.
One workaround to try would be to add a wrapper lambda which takes standardized args with pre-specified implicit coversions applied, e.g. T -> const T&, and possibly numeric types -> double.
Then, when you look up the function, you can cast it to use these standardized args, and the calling args will be implicitly converted.
This would rule out functions taking rvalue refs and non-const references, but I don't thing this is unreasonable for a function that you don't know the signature of, unless you want to disregard const-correctness completely.
Also, other implicit conversions wouldn't happen, e.g. Derived& -> Base&, or char* -> std::string, and I don't think there would be an easy way to make that happen without creating extra limitations.
Overall, it's definitely a tricky thing to do in c++, and anything you try will be hacky. This way should be decent enough. The performance overhead of one extra function call (which can be inlined), and possibly some extraneous argument conversions will be overshadowed by the unavoidable RTTI checking.
Here is a sample implementation (also here on ideone):
#include <unordered_map>
#include <typeinfo>
#include <typeindex>
#include <string>
#include <type_traits>
#include <iostream>
#include <assert.h>
#include <cxxabi.h>
#include <sstream>
#include <stdexcept>
template <typename Func, Func f>
struct store_func_helper;
// unix-specific
std::string demangle(const std::string& val) {
int status;
char *realname;
std::string strname = realname = abi::__cxa_demangle(val.c_str(), 0, 0, &status);
free(realname);
return strname;
}
// args will be implicitly converted to arg<T>::type before calling function
// default: convert to const Arg&
template <typename Arg, typename snifae=void>
struct arg {
using type = const Arg&;
};
// numeric types: convert to double.
template <typename Arg>
struct arg <Arg, typename std::enable_if<std::is_arithmetic<Arg>::value, void>::type> {
using type = double;
};
// set more special arg types here.
// Functions stored in the map are first wrapped in a lambda with this signature.
template <typename Ret, typename... Arg>
using func_type = Ret(*)(typename arg<Arg>::type...);
class func_map {
template <typename Func, Func f>
friend class store_func_helper;
public:
template <typename Func, Func f>
void store(const std::string& name){
store_func_helper<Func, f>::call(this, name );
}
template<typename Ret, typename... Args>
Ret call(std::string func, Args... args){
using new_func_type = func_type<Ret, Args...>;
auto& mapVal = m_func_map.at(func);
if (mapVal.second != std::type_index(typeid(new_func_type))){
std::ostringstream ss;
ss << "Error calling function " << func << ", function type: "
<< demangle(mapVal.second.name())
<< ", attempted to call with " << demangle(typeid(new_func_type).name());
throw std::runtime_error(ss.str());
}
auto typeCastedFun = (new_func_type)(mapVal.first);
//args will be implicitly converted to match standardized args
return typeCastedFun(std::forward<Args>(args)...);
};
private:
std::unordered_map<std::string, std::pair<void(*)(),std::type_index> > m_func_map;
};
#define FUNC_MAP_STORE(map, func) (map).store<decltype(&func),&func>(#func);
template <typename Ret, typename... Args, Ret(*f)(Args...)>
struct store_func_helper<Ret(*)(Args...), f> {
static void call (func_map* map, const std::string& name) {
using new_func_type = func_type<Ret, Args...>;
// add a wrapper function, which takes standardized args.
new_func_type lambda = [](typename arg<Args>::type... args) -> Ret {
return (*f)(args...);
};
map->m_func_map.insert(std::make_pair(
name,
std::make_pair((void(*)()) lambda, std::type_index(typeid(lambda)))
));
}
};
//examples
long add (int i, long j){
return i + j;
}
int total_size(std::string arg1, const std::string& arg2) {
return arg1.size() + arg2.size();
}
int main() {
func_map map;
FUNC_MAP_STORE(map, total_size);
FUNC_MAP_STORE(map, add);
std::string arg1="hello", arg2="world";
std::cout << "total_size: " << map.call<int>("total_size", arg1, arg2) << std::endl;
std::cout << "add: " << map.call<long>("add", 3, 4) << std::endl;
}

Attaching a "policy" to a function parameter

In some code that I am writing, I have a bunch of C++ functions that I am trying to bind to lua in a generic way. (However, this question really has nothing to do with lua, it's really a C++ design question.)
The idea is I might have a C++ function with signature
int my_function(lua_State * L, std::string server, std::string message);
for instance, and I want to be able to push it to lua and expose it to user scripts.
However, lua can only directly receive functions of signature int (lua_State *). So, I have some templates which take a function pointer with signature like the above, and produce a function of signature int(lua_State *), which tries to read corresponding arguments off of the lua stack, and calls the target function with the parameters if it succeeds, and signals a lua error for the user if not.
That part is working, with some work, and this question is not about how to do that. (Please don't tell me about luabind, luabridge or other existing libs, for reasons I can't go into those are not appropriate for my project.)
Instead the issue I'm having now is that sometimes I want the input parameters to have slightly different semantics.
For instance, sometimes a parameter should be optional. I specialized my template for boost::optional in order to handle that case. So I can tag optional parameters with boost::optional in the function signature and the wrapper will know that if that parameter is missing, it's not an error, and it should just pass boost::none. Example:
int my_function(lua_State * L, boost::optional<std::string>, std::string message);
So the boost::optional template is being used kind of like a "policy" for the input here, and I basically like how that is working.
Here's an issue I'm less sure about though: handling of bool. In lua, there is a proper boolean type, however, lua also has a notion of contextually boolean, similar to C++'s notion of contextually convertible to bool. In lua, the values false and nil are falsy, and all other values are truthy.
Typically, when you have a c++ function that takes a bool, the user will expect that they can pass it any value and that your interface will respect the truthiness even if it's not strictly speaking a boolean value. However, in other cases, you might really want it to be interpretted strictly as a bool, and for it to be a user error if they don't pass true or false.
What I would like to be able to do is tag the "strictness" policy within the function declaration, so it would look like
int my_function(lua_State * L, strict<bool> b, std::string message);
Where, strict is some template like
template <typename T>
struct strict {
T value;
};
and this template really has meaning only in my wrapper machinery.
The thing that is annoying about this is that then you have to type b.value everywhere.
I thought about doing it like this:
template <typename T>
struct strict {
T value;
operator T & () & { return this->value; }
operator const T & () const & { return this->value; }
operator T && () && { return std::move(this->value); }
};
to allow a bunch of ref-qualified implicit conversions from strict<T> to references to the value.
How unsafe is this? I don't see major safety holes in this, although I've always adhered to the "implicit conversions are evil" mantra. I played around with it a little in test code and it doesn't seem to create ambiguities or problems, but there might be a clever way to make this do something very bad that I didn't think of.
If it's not a good idea, is there a better strategy than typing b.value everywhere, or some different way of rigging up the parameter policies that won't intrude upon the types?
something like this should do it.
The overloads of visit are what do the work. Note the recursive call from the optional version.
#include <iostream>
#include <string>
#include <utility>
#include <iomanip>
#include <boost/optional.hpp>
// some boilerplate
template <typename T, template <typename, typename...> class Tmpl> // #1 see note
struct is_derived_from_template
{
typedef char yes[1];
typedef char no[2];
static no & test(...);
template <typename ...U>
static yes & test(Tmpl<U...> const &);
static bool constexpr value = sizeof(test(std::declval<T>())) == sizeof(yes);
};
template<typename T, template <typename, typename...> class Tmpl>
static constexpr bool is_derived_from_template_v = is_derived_from_template<T, Tmpl>::value;
// i dont know much about a lua_state but I guess it's a bit like this...
struct lua_state {
void set_string(std::size_t index, const std::string& s) {
std::cout << "index " << index << " setting string " << std::quoted(s) << std::endl;
}
void set_missing(std::size_t index) {
std::cout << "index " << index << " setting missing" << std::endl;
}
void set_int(std::size_t index, int i) {
std::cout << "index " << index << " setting int " << i << std::endl;
}
};
// policies
template<class T, std::enable_if_t<std::is_same<std::decay_t<T>, std::string>::value>* = nullptr>
void visit(std::size_t index, lua_state* pstate, T&& value)
{
pstate->set_string(index, std::forward<T>(value));
}
template<class T, std::enable_if_t<std::is_same<std::decay_t<T>, int>::value>* = nullptr>
void visit(std::size_t index, lua_state* pstate, T&& value)
{
pstate->set_int(index, std::forward<T>(value));
}
// special policy for optional
template<class T,
std::enable_if_t<is_derived_from_template_v<std::decay_t<T>, boost::optional>>* = nullptr>
void visit(std::size_t index, lua_state* pstate, T&& value)
{
if (value)
{
visit(index, pstate, std::forward<T>(value).value());
}
else {
pstate->set_missing(index);
}
}
// helper function
template<std::size_t...Is, class Tuple>
void set_vars_impl(lua_state* pstate, std::index_sequence<Is...>, Tuple&& tuple)
{
using expand = int [];
void(expand{ 0,
((visit(Is, pstate, std::get<Is>(std::forward<Tuple>(tuple)))),0)...
});
}
template<class...Ts>
void set_vars(lua_state* pstate, Ts&&...ts)
{
set_vars_impl(pstate,
std::make_index_sequence<sizeof...(Ts)>(),
std::make_tuple(std::forward<Ts>(ts)...));
}
int main(int argc, const char * argv[]) {
lua_state ls;
boost::optional<std::string> a { };
boost::optional<std::string> b { std::string { "hello" }};
std::string c = "world";
int d = 0;
boost::optional<int> e;
boost::optional<int> f { 1 };
set_vars(std::addressof(ls), a, b, c, d, e, f);
return 0;
}
expected results:
index 0 setting missing
index 1 setting string "hello"
index 2 setting string "world"
index 3 setting int 0
index 4 setting missing
index 5 setting int 1

Passing by value all types but string to a template function

I want to define a template function that gets one argument passed by value for all types but std::string (and const char*).
template<typename T>
void foo( T value )
{
// some code using value
}
The std::string version should behave exactly as the template version, but have its parameter passed by const&.
What is the best approach to do what I want without duplicating the body of foo()?
The best I was able to think is to wrap the code using value inside another function, and then call it inside all versions of foo() (the template version and the std::string overload). Is there another way? For example, is it possible to call the template version from within the std::string overload?
EDIT
What I want to know is a good rule of thumb for avoiding code duplication among various specializations and overloads. What is a good pattern to follow? Shall I define a wrapper function for the body and then call that from within all overloads/specializations, or there is another way?
In order to avoid code duplication, the answer by 101010 can be extended to actually call the template from within the overload:
#include <string>
#include <iostream>
#include <type_traits>
#include <boost/core/demangle.hpp>
template<typename T>
void foo( T value )
{
std::cout << "inside template" << std::endl;
std::cout << boost::core::demangle(typeid(value).name()) << std::endl;
}
void foo(const std::string &value)
{
std::cout << "inside const string overload" << std::endl;
foo<const std::string&>(value);
}
int main()
{
foo(10);
foo(std::string("hello"));
return 0;
}
output
inside template
int
inside const string overload
inside template
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >
live example
Simple solution: provide an overload for std::string:
void foo( std::string const &value ) {
// some code using value
}
I think what you are looking for is rvalue signature in C++ 11.
Its as simple as:
#include <iostream>
#include <string>
template<typename T>
void foo(T&& value)
{
std::cout << "was passed by refernece:" << std::is_lvalue_reference<T&&>::value << std::endl;
std::cout << value << std::endl;
}
int main()
{
std::string text = "hello";
foo(text);
foo(1);
}
You can either pass the parameter by reference or by value and the rvalue rules will use the appropriate type.
You can define a type-trait-like class that will convert std::string to std::string& and will keep the type for all other types:
template<class T>
struct helper {
typedef T type;
};
template<>
struct helper<std::string> {
typedef std::string& type; // or const std::string& type if you want
};
template<typename T>
void foo( typename helper<T>::type value, T value2 )
{
value = value2;
}
int main()
{
int a = 10;
foo(a, 42);
std::cout << a << std::endl; // prints 10
std::string s = "abc";
foo(s, std::string("def"));
std::cout << s << std::endl; // prints def
}
Full example: http://coliru.stacked-crooked.com/a/96cf78e6c4846172
UPD: as noted by #PiotrSkotnicki, having only one parameter makes type-deduction fail. However, I will keep the answer as it might be helpful in case you indeed have several parameters of type T or if you are ok with specifying explicit template parameter to foo.
UPD2: To solve the type-deduction problem, you may add another wrapper:
template<typename T>
void foo_helper( typename helper<T>::type value )
{
value = T();
}
template<typename T>
void foo(T& value)
{
foo_helper<T>(value);
}
This still might have some problems, so whether this is applicable to your usecase, is up to you to decide.
use std::enable_if + std::is_convertibale:
template<typename T>
typename std::enable_if<!std::is_convertible<T,std::string>::value>::type foo( T value )
{
// some code using value
}

Default value for function argument which is pointer to member

I'm trying to implement decorator for functions using variadic templates. And try to minimize number of overloads since it reduce the size of a compiler error messages in case of template params deducion failure. I have implementation which works with any member of any class (works with delegate Ret (Ctx::*member)(Args...)) and one extra overload which do not take pointer to member function and just calls first version passing &Ctx::opeator(). I've tried to remove extra overload by adding default value for member pointer argument here. Here is the version which compiles:
template<
typename Decorator,
typename Ctx, typename R, typename... Args,
typename... CTorArgs
>
Decorator decorate(
CTorArgs&&...cargs,
const Ctx &obj, R (Ctx::*member)(Args...) const = &Ctx::operator()
) {
return Decorator(
std::function<R(Args...)>([&obj, member](Args... args){
return (obj.*member)(args...);
}),
std::forward(cargs)...
);
}
But it can't be called without explicit member function specification. Here is an example I'm playing with right now:
struct IntDecorator {
IntDecorator(std::function<std::string(std::string)> func):
m_func(func) {}
std::string operator() (int val) {
std::ostringstream oss;
oss << val;
return m_func(oss.str());
}
private:
std::function<std::string(std::string)> m_func;
};
struct PrefixAdder {
PrefixAdder(std::string prefix): m_prefix(prefix) {}
std::string addPrefix(std::string val) const {return m_prefix + val;}
std::string operator() (std::string val) const {return m_prefix + val;}
private:
std::string m_prefix;
};
int main(int, char**) {
PrefixAdder p("+++> ");
std::cout << decorate<IntDecorator>(p, &PrefixAdder::addPrefix)(123) << std::endl;
std::cout << decorate<IntDecorator>(p, &PrefixAdder::operator())(123) << std::endl;
std::cout << decorate<IntDecorator>(p)(123) << std::endl; // compilation error
}
compiler can't deduce type R (member function return type). The code works after adding extra overload which I try to elliminate or by specifying all template parameters which is even worse than extra overload:
template<typename Decorator, typename Ctx, typename... CTorArgs>
Decorator decorate(CTorArgs&&...cargs, const Ctx &obj) {
return decorate<Decorator>(cargs..., obj, &Ctx::operator());
}
Can I have automatic type dedution without additional version of decorate template? What is the problem to deduce types automatically here in C++11?
Edit: My initial aim was to have better error message and I've achieved it using type traits and static_assert. So this question is more theoretical then practical for now. If somebody knows how to write one implementation which covers delegate and functor you can provide an answer and I'll accept it.

Deducing the return type of a map with varying value types

I have the following code
#include <boost\any.hpp>
#include <iostream>
#include <memory>
#include <map>
#include <string>
enum class tags { int_param, string_param };
class Parameters
{
public:
template <typename T>
void set(tags key, T value)
{
map_[key] = value;
}
template <typename T>
T get(tags key)
{
return boost::any_cast<T>(map_[key]);
}
private:
std::map<tags, boost::any> map_;
};
int main()
{
Parameters params;
params.set(tags::int_param, 42);
params.set(tags::string_param, "it works!");
int int_par = params.get<int>(tags::int_param);
std::string string_par = params.get<std::string>(tags::string_param);
std::cout << "Channel: " << int_par << std::endl;
std::cout << "Filename: " << string_par << std::endl;
}
This code works however I do not like having to supply the templated type and I would like, if at all possible, to be able to retrieve something from the map without having to supply the template type i.e.
int int_par = params.get(tags::int_param);
instead of
int int_par = params.get<int>(tags::int_param);
I understand by using boost::any that we need to cast to a supplied type from the any type but is there a way we can do it without having to supply it ourself and can we instead deduce it some other way>
If you change tags from enum values to structs you can achieve the desired behavior and even other good features, such as type safety and that our use of any_cast won't ever throw.
By using structs we can define simple traits to get each tags value type, such as
enum class tag_id
{
int_param; // Id which represents int_param tag on std::map
}
struct int_param_t
{
using value_type = int;
static const tag_id id;
};
const tag_id int_param_t::id = tag_id::int_param;
Now we can use this traits to achieve your desired syntax
template< typename Tag >
auto get( Tag ) -> typename Tag::valye_type
{
return boost::any_cast<typename Tag::valye_type>(map_[Tag::id]);
}
// ...
int a = parameters.get( int_param_t{} ); // Ok, returns int.
std::string b = parameters.get( int_param_t{} ); // Error, int is not convertible to std::string.
As a bonus we can use this traits to ensure only types convertable to the correct type are used with set function
template< typename Tag >
void set( Tag, typename Tag::value_type value )
{
map_[Tag::id] = value;
}
// ...
parameter.set( int_param_t{}, 0 ); // Ok, 0 is convertible to int.
parameter.set( int_param_t{}, "string" ); // Error, const char[] is not convertible to int.
To make it a little bit prettier, I would also define some alias and constants such as
// Helper alias to avoid writing typename everywhere.
template< typename T >
using param_type = typename T::value_type;
// Used to avoid having to create tag objects every function call.
constexpr int_param_t int_param{};
And here is the final product
#include <boost/any.hpp>
#include <iostream>
#include <memory>
#include <map>
#include <string>
namespace tags
{
enum class tag_id
{
int_param,
string_param
};
struct int_param_t
{
using value_type = int;
static const tag_id id;
};
const tag_id int_param_t::id = tag_id::int_param;
constexpr int_param_t int_param{};
struct string_param_t
{
using value_type = std::string;
static const tag_id id;
};
const tag_id string_param_t::id = tag_id::string_param;
constexpr string_param_t string_param{};
}
// Helper alias to avoid writing typename everywhere.
template< typename T >
using param_type = typename T::value_type;
class Parameters
{
public:
template <typename Tag>
void set(Tag, param_type< Tag > value)
{
map_[Tag::id] = value;
}
template <typename Tag>
auto get(Tag) -> param_type< Tag >
{
return boost::any_cast< param_type< Tag > >(map_[Tag::id]);
}
private:
std::map<tags::tag_id, boost::any> map_;
};
int main()
{
Parameters params;
params.set(tags::int_param, 42);
params.set(tags::string_param, "it works!");
int int_par = params.get(tags::int_param);
std::string string_par = params.get(tags::string_param);
std::cout << "Channel: " << int_par << std::endl;
std::cout << "Filename: " << string_par << std::endl;
}
There is no way to deduce anything from an any but there might be a chance to deduce something from the declaration. As your code already throws when any_cast fails, I suppose this is not an issue for you.
You could extend (really a bad idea to due to the missing virtual destructor) boost::any or wrap it and provide a templated conversion operator.
#include <boost/any.hpp>
#include <iostream>
struct converting_any {
converting_any(boost::any* a) : a(a) {}
template<typename T>
operator T() { return boost::any_cast<T>(*a); } // dereference to get the throwing version
private:
boost::any* a; // wrapped any
};
int main()
{
int i = 3; std::string j = "asddf";
boost::any a = i;
int i2 = converting_any(&a);
try {
std::string j2 = converting_any(&a);
} catch(...) {
std::cout << "Failure." << std::endl;
}
a = j;
std::string j3 = converting_any(&a);
return 0;
}
You could return a reference wrapper which postpones the cast until its needed:
struct converter {
converter(boost::any & any) : any(any) {}
template <typename T>
operator T() {return boost::any_cast<T>(any);}
boost::any & any;
};
converter get(tags key)
{
return map_[key];
}
The template argument can be deduced from the type being converted to, so your int example will work without an explicit template argument.
Note that, as with your version, this will fail if the template parameter doesn't match the variant's type, as demonstrated in your example when attempting to convert const char * to std::string.