Контейнер для разных функций?

Я пытаюсь реализовать класс контейнера для разных функций, где я могу удерживать указатели на функции и использовать его для вызова этих функций позже. Я попытаюсь более точно описать свою проблему.

В качестве примера у меня есть две различные тестовые функции:

int func1(int a, int b) { 
    printf("func1 works! %i %i\n", a, b);
    return 0;
}
void func2(double a, double b) {
    printf("func2 works! %.2lf %.2lf\n", a, b);
}

и у меня также есть массив вариантов, который содержит аргументы функции:

std::vector<boost::variant<int, double>> args = {2.2, 3.3};

Я решил использовать свой собственный класс functor, полученный из некоторого базового класса (я думал об использовании виртуальных методов):

class BaseFunc {
public:
    BaseFunc() {}
    ~BaseFunc() {}
};

template <typename T>
class Func;

template <typename R, typename... Tn>
class Func<R(Tn...)> : public BaseFunc {
    typedef R(*fptr_t)(Tn...);
    fptr_t fptr;
public:
    Func() : fptr(nullptr) {}
    Func(fptr_t f) : fptr(f) {}
    R operator()(Tn... args) {
        return fptr(args...);
    }
    Func& operator=(fptr_t f) {
        fptr = f;
        return *this;
    }
};

Также я решил сохранить некоторую информацию о функции и ее аргументах:

struct TypeInfo {
    int type_id; // for this example: 0 - int, 1 - double

    template <class T>
    void ObtainType() {
        if (std::is_same<void, T>::value)
            type_id = 0;
        else if (std::is_same<int, T>::value)
            type_id = 1;
        else if (std::is_same<double, T>::value)
            type_id = 2;
        else
            type_id = -1;
    }
};

struct FunctionInfo {
public:
    FunctionInfo() {}
    FunctionInfo(BaseFunc *func, const TypeInfo& ret, std::vector<TypeInfo>& args) :
        func_ptr(func), return_info(ret)
    {
        args_info.swap(args);
    }
    ~FunctionInfo() {
        delete func_ptr;
    }

    BaseFunc * func_ptr;
    TypeInfo return_info;
    std::vector<TypeInfo> args_info;
};

Итак, теперь я могу определить класс контейнера:

class Container {
private:
    template <size_t n, typename... T>
    void ObtainTypeImpl(size_t i, TypeInfo& t)
    {
        if (i == n)
            t.ObtainType<std::tuple_element<n, std::tuple<T...>>::type>();
        else if (n == sizeof...(T)-1)
            throw std::out_of_range("Tuple element out of range.");
        else
            ObtainTypeImpl<(n < sizeof...(T)-1 ? n + 1 : 0), T...>(i, t);
    }
    template <typename... T>
    void ObtainType(size_t i, TypeInfo& t)
    {
        return ObtainTypeImpl<0, T...>(i, t);
    }
public:
    template <class R, class ...Args>
    void AddFunc(const std::string& str, R(*func)(Args...)) {
        BaseFunc * func_ptr = new Func<R(Args...)>(func);
        size_t arity = sizeof...(Args);
        TypeInfo ret;
        ret.ObtainType<R>();
        std::vector<TypeInfo> args;
        args.resize(arity);
        for (size_t i = 0; i < arity; ++i)
        {
            ObtainType<Args...>(i, args[i]);
        }
        cont_[str] = FunctionInfo(func_ptr, ret, args);
    }
    void CallFunc(const std::string& func_name, 
                  std::vector<boost::variant<int, double>>& args_vec) {
        auto it = cont_.find(func_name);
        if (it != cont_.end())
        {
            // ???????
            // And here I stucked
        }
    }
private:
    std::map<std::string, FunctionInfo> cont_;
};

И затем я застрял.

  • Не знаю, как получить информацию о типе функции из моей структуры:).
  • Не знаю, как преобразовать вектор вариантов в список аргументов.

Может, мой путь был неправильным? Можете ли вы предложить какое-либо решение этой проблемы, кроме script двигателя, такого как Lua?

Ответ 1

Вы можете сделать что-то вроде:

class BaseFunc {
public:
    virtual ~BaseFunc() = default;

    virtual void Call(std::vector<boost::variant<int, double>>& args_vec) const = 0;
};

template <typename F> class Function;

template <typename R, typename... Args> class Function<R(Args...)> : public BaseFunc
{
public:
    Function(R (*f)(Args...)) : f(f) {}
    void Call(std::vector<boost::variant<int, double>>& args_vec) const override
    {
        Call(args_vec, std::index_sequence_for<Args...>());
    }
private:
    template <std::size_t ... Is>
    void Call(
        std::vector<boost::variant<int, double>>& args_vec,
        std::index_sequence<Is...>) const
    {
        // Add additional check here if you want.
        f(boost::get<Args>(args_vec.at(Is))...);
    }

private:
    R (*f)(Args...);
};

Живой пример