2014-12-15 8 views
10

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

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

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}; 

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

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_; 
}; 

А потом я stucked.

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

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

+0

Я думаю, что я видел эту проблему в более эффективной C++ Скотт Майерс. Я полагаю, и он предоставил решение. – DumbCoder

+0

Что делать, если именованная функция принимает разные аргументы? Сбой или отличные значения для ожидаемого типа? – Useless

ответ

2

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

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...); 
}; 

Live example

Смежные вопросы