2014-12-15 12 views
10

Sto provando ad implementare una classe contenitore per diverse funzioni in cui posso tenere i puntatori di funzione e usarla per richiamare quelle funzioni in seguito. Proverò a descrivere il mio problema in modo più preciso.Contaner per diverse funzioni?

Per esempio, ho 2 diverse funzioni di test:

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

e ho anche gamma di varianti, che detiene gli argomenti della funzione:

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

ho deciso di usare il mio funtore classe derivata da una certa classe di base (Avevo pensato di usare metodi virtuali):

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

anche io' ve deciso di memorizzare alcune informazioni sulla funzione ei suoi argomenti:

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

Così ora posso definire una classe contenitore:

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

E poi ho stucked.

  1. Non so come ottenere informazioni sul tipo di funzione dalla mia struct :).
  2. Non so come convertire il vettore di varianti nell'elenco di argomenti.

Forse il mio percorso era sbagliato? Puoi suggerire qualsiasi soluzione di questo problema tranne il motore di script come Lua?

+0

Credo di aver visto questo problema in modo più efficace C++ da Scott Myers. E lui ha fornito una soluzione, credo. – DumbCoder

+0

Cosa dovrebbe fare se la funzione denominata accetta argomenti diversi? Fallisci o converti i valori nel tipo previsto? – Useless

risposta

2

Si può fare qualcosa di simile:

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

Problemi correlati