3
\$\begingroup\$

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;
}
\$\endgroup\$
  • \$\begingroup\$ Follow-up question. If this code is obsolete, you might as well delete the question. \$\endgroup\$ – 200_success May 1 '16 at 5:36
  • \$\begingroup\$ @200_success, really i would like to keep it, if you not mind, i have few ideas to improve it as it is, also, i'm working on game that's replied on this approach. the second question i made it as alternative but it would not be useful as much as this one. \$\endgroup\$ – MORTAL May 1 '16 at 18:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.