2
\$\begingroup\$

Based on the previous question, I have improved it to make it insert regular function. Also, I have implemented a new template approach to obtain the signature from any function by testing its operator().

I would like to know how I can improve it further.

#include <functional>
#include <typeindex>
#include <string>
#include <tuple>
#include <map>
#include <cassert>
#include <iostream>
#include <memory>

////////////////////////// Meta Data ////////////////////////////////
template<typename F> 
struct Signature_helper;

template<typename F, typename R, typename... Ts>
struct Signature_helper<R(F::*)(Ts...) const>
{
    using type = R(Ts...);
    using result_type = R;
};

template<typename F>
#define TRAIT_CASE decltype(&std::remove_reference<F>::type::operator())
struct Signature
{
    using type = typename Signature_helper<TRAIT_CASE>::type;
    using result_type = typename Signature_helper<TRAIT_CASE>::result_type;
};

template<typename R, typename... Ts>
struct Signature<R(&)(Ts...)>
{
    using type = R(Ts...);
    using result_type = R;
};

////////////////////////// Any Class ////////////////////////////////
class AnyFunction
{
    struct placeholder
    {
        virtual ~placeholder() = default;
    };

    template <typename F>
    struct holder : placeholder
    {
        holder(F&& f) : data(std::forward<F>(f)) {}
        F data;
    };

public:
    template <typename F>
    AnyFunction(F&& f)
        : mContent(std::make_shared<holder<std::decay_t<F>>>(std::forward<F>(f)))
    {
    }

    template <typename F>
    const F& cast() const
    {
        assert(dynamic_cast<const holder<F>*>(mContent.get()) != nullptr);
        return static_cast<const holder<F>&>(*mContent).data;
    }

private:
    std::shared_ptr<placeholder> mContent = nullptr;
};

////////////////////////// Function Holder //////////////////////////
class FunctionHolder
{
public:
    template <typename F>
    void insert(F&& f)
    {
        using function = std::function<typename Signature<F>::type>;
        using type = typename Signature<F>::result_type;

        auto result = mHolder.emplace(typeid(type), function(std::forward<F>(f)));
        if (!result.second)
        {
            std::cerr << "ERROR: key "
                + std::string(typeid(typename Signature<F>::result_type).name())
                + " was already inserted\n";
        }
    }

    template <typename... Ts>
    decltype(auto) call(Ts&&... ts) const
    {
        using type = typename std::tuple_element<0, std::tuple<Ts...>>::type;
        auto it = mHolder.find(typeid(type));
        assert(it != mHolder.end());
        return it->second.cast<std::function<type(Ts...)>>()(std::forward<Ts>(ts)...);
    }

private:
    std::map<std::type_index, AnyFunction> mHolder;
};

////////////////////////// Test /////////////////////////////////////
struct Functor
{
    int operator()(int i, int j) const
    {
        return i + j;
    }
};

float function(float i, float j)
{
    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, double j) { return i * j; };
    Functor functor;

    FunctionHolder holder;

    holder.insert(fn);
    holder.insert(lambda);
    holder.insert(functor);
    holder.insert(function);

    std::cout << holder.call(std::string("World!"), 2016) << std::endl;
    std::cout << holder.call(1.5, 2.0) << std::endl;
    std::cout << holder.call(1, 2) << std::endl;
    std::cout << holder.call(6.f, 3.f) << std::endl;
}
\$\endgroup\$

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.