I have a VariantType
that can be empty, i.e. has a void state.
The following code, when compiling with Mingw Builds x64 5.3.0 generates the error:
error: conversion from 'void' to non-scalar type 'VariantType {aka utils::Variant<bool, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >}' requested|
How can I avoid the error:
#include <Common/Variant.hpp>
using namespace utils;
#include <vector>
#include <unordered_map>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <map>
using VariantType = Variant<bool,int,std::string>;
class EFBase
{
public:
virtual ~EFBase() {}
};
template<typename FuncSig>
class EF : public EFBase
{
public:
EF(std::function<FuncSig> func) : m_function(func) {};
std::function<FuncSig> m_function;
};
class Functions
{
public:
using FuncMap = std::map<std::string,EFBase*>;
FuncMap m_functions;
template<typename FuncType>
void Add(const std::string name, FuncType function)
{
m_functions.emplace(FuncMap::value_type(name,new EF<FuncType>(std::function<FuncType>(function))));
}
template<typename... Args>
VariantType Invoke(const std::string& name, Args... args)
{
auto itr = m_functions.find(name);
if(itr == m_functions.end())
return VariantType();
typedef void Func(typename std::remove_reference<Args>::type...);
std::type_index index(typeid(Func));
const EFBase& a = *itr->second;
std::function<Func> func = static_cast<const EF<Func>&>(a).m_function;
if(typeid(typename std::function<Func>::result_type) == typeid(void))
{
func(std::forward<Args>(args)...);
return VariantType();
}
else
{
VariantType x = func(std::forward<Args>(args)...);
return x;
}
}
};
int main()
{
Functions f;
f.Add<int(bool)>("get3",[](bool x) { return 3; });
VariantType v = f.Invoke<>("get3",true);
return 0;
}
I would've thought that the check for the result_type
of the function object would have been enough; but I guess it's not because of the template instantiation. Do I need a sort of helper struct that does something different in a void case (based on template params)?
The purpose of the code is to store functions of arbitrary signature in a map by name so that they can be called upon later. The VariantType
handles the multiple possible return values.
The error is on the assignment in the else
block of the Invoke
method.
The VariantType
is a fair bit of code, so I didn't provide it (I'm not sure it's pertinent anyway). But I can if needed.
Depending on the exact use case, one suggestion would be to store std::function<VariantType(x)>
s in the map (with x
being some fixed set of parameters), with the idea that the functions will be adapted to a particular signature using functor wrappers for storage. The client's functions passed to Add
would then either (a) need to be wrapped in functors that have the correct signature, or (b) if you know all of the different kinds of functions that will ever be passed to Add
, you could define template<typename F, typename ...Args> Add(F f)
, and specialize it based on std::result_of<F(...Args)>
so that Add
could make the wrappers. You could also do a mixed approach and require that the clients pass functions that conform to the fixed parameter list and Add
can wrap those functions to return VariantType
s based on the passed-in function's return type.
An example is below showing some of the concepts.
Note the SFINAE principle being applied to the wrapper
template overloads to avoid having the compiler fail in the specialization that we don't want it to evaluate (the one where f
returns void
).
Also note that I think a situation where you actually need to dispatch different argument lists at runtime depending on the type of function may be a lot more difficult, hence the approach here tries to normalize the argument lists by capturing them when creating the callback. If you really think you need the variadic argument list in Invoke
, then I would suggest maybe looking into using boost::any
to wrap the functions, or at least for conceptual guidance.
#include <iostream>
#include <type_traits>
#include <functional>
#include <string>
#include <map>
#include <vector>
template<typename T1, typename T2, typename T3>
struct Variant
{
Variant() { std::cout << "In void Ctor of Variant" << std::endl; }
template<typename T> Variant(T t) { std::cout << "In data Ctor of Variant" << std::endl; }
};
using VariantType = Variant<bool,int,std::string>;
using FuncSig = std::function<VariantType(int)>;
struct Functions
{
template<typename F, typename Result = typename std::result_of<F(int)>::type >
void Add(const std::string name, F f)
{
this->m_functions.emplace(name, [=](int i) { return wrapper<F, Result>(f, i); });
}
VariantType Invoke(const std::string& name, int i)
{
auto itr = m_functions.find(name);
if(itr == m_functions.end())
return VariantType();
return itr->second(i);
}
private:
using FuncMap = std::map<std::string,FuncSig>;
FuncMap m_functions;
template<typename F, typename ReturnValue, typename ...Args>
static typename std::enable_if<!std::is_same<ReturnValue, void>::value, VariantType>::type wrapper(F f, Args&&... args)
{
VariantType x = f(std::forward<Args>(args)...);
return x;
}
template<typename F, typename ReturnValue, typename ...Args>
static typename std::enable_if<std::is_same<ReturnValue, void>::value, VariantType>::type wrapper(F f, Args&&... args)
{
f(std::forward<Args>(args)...);
return VariantType();
}
};
struct Client
{
Client(Functions& funcs)
{
funcs.Add("v_func", [&](int i) { this->v_func(this->d, i); } );
funcs.Add("b_func", [&](int i) { return this->b_func(i); } );
funcs.Add("s_func", [&](int i) { return this->s_func(i, this->some_string); } );
funcs.Add("i_func", [&](int i) { return this->i_func(i); } );
}
void v_func(double d, int i) const { std::cout << this->obj_name << ": v_func()" << d << ", " << i << std::endl; }
bool b_func(int i) const { std::cout << this->obj_name << ": b_func()" << i << std::endl; return i > 5; }
std::string s_func(int i, std::string const& s) const { std::cout << this->obj_name << ": s_func()" << i << ", " << s << std::endl; return s; }
int i_func(int i) const { std::cout << this->obj_name << ": i_func()" << i << std::endl; return i + 10; }
std::string obj_name;
const std::string some_string = "some_string";
const double d = 3.14;
};
int main()
{
VariantType variant;
Functions functions;
Client c(functions);
c.obj_name = "Client1";
std::vector<std::string> v = { "s_func", "b_func", "v_func", "i_func" };
int i = 0;
for (auto s : v) { variant = functions.Invoke(s, i++); }
return 0;
}
outputs:
In void Ctor of Variant
Client1: s_func()0, some_string
In data Ctor of Variant
Client1: b_func()1
In data Ctor of Variant
Client1: v_func()3.14, 2
In void Ctor of Variant
Client1: i_func()3
In data Ctor of Variant
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments