2014-12-15 20 views
10

Próbuję zaimplementować klasę kontenerów dla różnych funkcji, w których mogę przechowywać wskaźniki funkcji i używać ich do wywoływania tych funkcji później. Spróbuję lepiej opisać mój problem.Contaner dla różnych funkcji?

Jako przykład mam 2 różne funkcje testowe:

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

i mam również szereg wariantów, które posiada argumentów funkcji:

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

Zdecydowałem się użyć własnego funktor klasa wywodząca się z pewnej klasy bazowej (myślałem o użyciu metod wirtualnych):

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

Również ja " ve postanowił przechowywać pewne informacje na temat funkcji i jej argumenty:

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

Więc teraz mogę zdefiniować klasę w opakowaniu:

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

I wtedy stucked.

  1. Nie wiem, jak uzyskać informacje o typie funkcji z mojej struktury :).
  2. Nie wiem, jak przekonwertować wektor wariantów na listę argumentów.

Może moja ścieżka była zła? Czy możesz zaproponować rozwiązanie tego problemu, z wyjątkiem silnika skryptów, takiego jak Lua?

+0

Chyba widziałem ten problem w sposób bardziej efektywny C++ Scott Myers. I wierzę, że zapewnił rozwiązanie. – DumbCoder

+0

Co powinien zrobić, jeśli nazwana funkcja przyjmuje różne argumenty? Błąd lub rzut wartości do oczekiwanego typu? – Useless

Odpowiedz

2

można zrobić coś takiego:

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

Powiązane problemy