I was inspired from question here, to implement function container that holder any kind of function by using boost::any, I have carried on and extend it further to make the functions holder accept lambda and functor. Also, I have implemented my own any class (AnyFunction) which is seemed work perfectly.
I would like to know how can I improve it further?
#include <functional>
#include <typeindex>
#include <string>
#include <tuple>
#include <map>
#include <cassert>
#include <iostream>
#include <memory>
template <class...>
using void_t = void;
template <class C, class X = void_t<>>
struct has_result_type : std::false_type {};
template <class C>
#define TRAIT_CASE decltype(std::declval<typename C::result_type>())
struct has_result_type<C, void_t<TRAIT_CASE>> : std::true_type {};
class AnyFunction
{
struct placeholder
{
virtual ~placeholder() {}
};
template <typename T>
struct holder : placeholder
{
holder(const T& t) : data(t) {}
T data;
};
public:
template <typename T>
AnyFunction(T&& t) :
mContent(std::make_shared<holder<std::decay_t<T>>>(std::forward<T>(t)))
{
}
template <typename T>
const T& cast() const
{
assert(dynamic_cast<const holder<T>*>(mContent.get()) != nullptr);
return static_cast<const holder<T>&>(*mContent).data;
}
private:
std::shared_ptr<placeholder> mContent = nullptr;
};
class FunctionHolder
{
public:
template <typename F>
void insert(const F& f)
{
insertImpl<F>(f, &F::operator());
}
template <typename... Ts>
decltype(auto) call(Ts&&... ts) const
{
using type = typename std::tuple_element<0, std::tuple<Ts...>>::type;
auto it = mFunctionHolder.find(typeid(type));
assert(it != mFunctionHolder.end());
return it->second.cast<std::function<type(Ts...)>>()(std::forward<Ts>(ts)...);
}
private:
template
<
typename F,
typename R,
typename... Ts,
typename = std::enable_if_t<!has_result_type<F>::value>
>
void insertImpl(const F& f, R(F::*)(Ts...) const)
{
std::function<R(Ts...)> temp = f;
insertImpl(typeid(R), temp);
}
template <typename F, typename = std::enable_if_t<has_result_type<F>::value>>
void insertImpl(const F& f, bool)
{
insertImpl(typeid(typename F::result_type), f);
}
template <typename F>
void insertImpl(const std::type_index& key, const F& f)
{
auto result = mFunctionHolder.emplace(key, f);
if (!result.second)
{
std::cerr << "ERROR: key "
+ std::string(key.name())
+ " was already inserted\n";
}
}
private:
std::map<std::type_index, AnyFunction> mFunctionHolder;
};
struct Functor
{
int operator()(int i, int j) const
{
return i + j;
}
};
int main()
{
std::function<std::string(std::string, int)> fn = [](const std::string& s, int i)
{
return "Hello " + s + " " + std::to_string(i);
};
auto lambda = [](double i, int j) { return i * j; };
Functor functor;
FunctionHolder holder;
holder.insert(fn);
holder.insert(lambda);
holder.insert(functor);
std::cout << holder.call(1.5, 2) << std::endl;
std::cout << holder.call(1, 2) << std::endl;
std::cout << holder.call(std::string("World!"), 2016) << std::endl;
}