1

J'ai écrit du code pour stocker certaines tâches avec ses paramètres pour une exécution ultérieure. Code:Impossible de transmettre les paramètres par "variables" au modèle basé sur les variables

class TaskInterface 
{ 
public: 
    virtual void Execute() = 0; 
}; 

namespace TaskHelper 
{ 
    template <std::size_t... Types> 
    struct index {}; 

    template <std::size_t N, std::size_t... Types> 
    struct gen_seq : gen_seq<N - 1, N - 1, Types...> {}; 

    template <std::size_t... Types> 
    struct gen_seq<0, Types...> : index<Types...>{}; 
} 

template <typename ReturnType, typename... Types> 
class SimpleTask : public TaskInterface 
{ 
public: 
    template <typename Function> 
    SimpleTask(Function&& func, Types&&... args) 
     : m_function(std::forward<Function>(func)), 
     m_args(std::make_tuple(std::forward<Types>(args)...)) { 
    } 

    void Execute() override final 
    { 
     func(m_args); 
    } 

private: 
    std::function<ReturnType(Types...)> m_function; 
    std::tuple<Types...> m_args; 

    template <typename... Args, std::size_t... Is> 
    void func(std::tuple<Args...>& tup, TaskHelper::index<Is...>) 
    { 
     m_function(std::get<Is>(tup)...); 
    } 

    template <typename... Args> 
    void func(std::tuple<Args...>& tup) 
    { 
     func(tup, TaskHelper::gen_seq<sizeof...(Args)>{}); 
    } 
}; 

template < typename ReturnType, class Class, typename... Types> 
class MemberTask : public TaskInterface 
{ 
public: 
    typedef ReturnType(Class::*Method)(Types...); 

    MemberTask(Class* object, Method method, Types&&... args) : 
     m_object(object), m_method(method), m_args(std::make_tuple(std::forward<Types>(args)...)) { 
    }; 

    void Execute() override final 
    { 
     func(m_args); 
    }; 

private: 
    Class* m_object; 
    Method m_method; 
    std::tuple<Types...> m_args; 

    template <typename... Args, std::size_t... Is> 
    void func(std::tuple<Args...>& tup, TaskHelper::index<Is...>) 
    { 
     (m_object->*m_method)(std::get<Is>(tup)...); 
    } 

    template <typename... Args> 
    void func(std::tuple<Args...>& tup) 
    { 
     func(tup, TaskHelper::gen_seq<sizeof...(Args)>{}); 
    } 
}; 

template <typename Function, typename... Arguments> 
TaskInterface* CreateSimpleTask(Function&& func, Arguments&&... args) 
{ 
    return new SimpleTask<typename std::result_of<decltype(func)(Arguments...)>::type, Arguments...>(std::forward<Function>(func), std::forward<Arguments>(args)...); 
} 

template <class Class, typename Method, typename... Arguments> 
TaskInterface* CreateMemberTask(Class* obj, Method method, Arguments&&... args) 
{ 
    return new MemberTask<typename std::result_of<decltype(method)(Class)>::type, Class, Arguments...>(std::forward<Class*>(obj), std::forward<Method>(method), std::forward<Arguments>(args)...); 
} 

class Test { 
public: 
    Test() { id = ++m_id; } 
    bool doIt(int n) { 
     std::cout << "doIt of " << n * id; 
     return true; 
    }; 

private: 
    static int m_id; 
    int id; 
}; 

int Test::m_id = 0; 


double test1(int xs) 
{ 
    xs *= 555; 
    return 66.02l; 
} 

Mais le problème est que je peux créer ces tâches que dans cette façon:

TaskInterface* st = CreateSimpleTask(test1, 5); 

Test t; 
TaskInterface* mt = CreateMemberTask(&t, &Test::doIt, 66); 

et ne peut pas dans ces termes:

// error C2664: 'double (int)' : cannot convert argument 1 from 'int *' to 'int' 
int xxxx; 
TaskBase* st = CreateSimpleTask(test1, &xxxx); 

ou pour MemberTask:

// cannot convert argument 2 from 'bool (__thiscall Test::*)(std::string)' to 'bool (__thiscall Test::*)(std::string &)' 
std::string ss = "sdfsdf"; 
TaskBase* mt = CreateMemberTask(&t, &Test::doIt, ss); 

Comment puis-je modifier m y classes afin de passer des paramètres non seulement par "valeur", mais aussi par "variables"? Ou toute ma "architecture" est complètement fausse dans ce but?

+1

Vous devriez probablement utiliser 'std :: bind' ou lambda ici, avec' std :: function' si vous voulez les stocker, cela rendrait votre code beaucoup plus simple. – Holt

+0

La ligne réelle et les erreurs complètes sont utiles. Si vous ne comprenez pas les erreurs, les modifier est une mauvaise idée. – Yakk

+0

Vous pouvez jeter un oeil à [cette réponse] (http://stackoverflow.com/a/20441189/1023390) qui correspond à [std :: apply] de C++ 17 (http: // en. cppreference.com/w/cpp/utility/apply). – Walter

Répondre

1

Une approche beaucoup plus simple que de stocker une fonction avec une certaine signature et ses arguments, est simplement de stocker une fonction sans arguments et son contexte (parfois appelé thunk).

std::function<void()> st = [] { test1(5); }; 
std::function<void()> mt = [&] { t.doIt(ss); }; 

Ceci est beaucoup plus simple et ne fuit pas, par exemple, contrairement à votre code. Vous pouvez capturer le contexte soit par valeur, par référence ou par combinaison. Les Lambdas sont cool! http://en.cppreference.com/w/cpp/language/lambda.