Conteneur pour les pointeurs vers les fonctions membres avec des arguments différents

Je cherche partout (Design & Co modernes en C ++), mais je ne trouve pas un moyen agréable de stocker un ensemble de rappels acceptant des arguments différents et opérant sur des classes différentes. J’ai besoin de cela car je voudrais que chaque object de mon application ait la possibilité de reporter l’exécution de l’une de ses méthodes sur un object d’ Clock maître qui, en gardant une trace de l’heure actuelle, peut appeler ces méthodes au bon moment. Le code que je vise est quelque chose du genre:

Dans la void executeAction1Deferred(int time, int arg1, bool arg2) de class1 , où heure est l’heure d’exécution souhaitée dans le futur, il devrait exister quelque chose comme ceci:

 Clock::defer(time, Class1::action1, this, arg1, arg2); 

Dans Clock::defer(??? what signature ????) un object qui représente cette tâche est stocké dans une queue prioritaire où le temps est la clé. Pour chaque quantum d’ Clock la liste des tâches est ensuite parcourue et les tâches qui doivent être exécutées dans ce quantum seront exécutées. Notez que j’ai utilisé “différer” en tant que fonction statique parce que j’ai l’intention de l’object Clock d’un singleton, mais il peut également s’agir d’une fonction membre, c’est simplement une question de choix.

J’ai pensé utiliser void* pour conserver un nombre variable d’arguments, mais avoir ma méthode action1() acceptant un void* est assez terrible, également parce que j’aurais besoin de créer une structure pour l’argument chaque fois que j’utilise directement cette fonction sans le différer.

J’ai fait face à ces problèmes à plusieurs resockets dans le passé et je n’ai jamais trouvé de solution vraiment décente. Veuillez noter qu’il s’agit d’un petit projet multi-plateforme où la simplicité de construction pour les programmeurs inexpérimentés qui pourraient l’étendre est essentielle, je ne veux pas utiliser boost. Mais tous les compilateurs des plates-formes std::tr1 nous nous adressons ont une liaison std::tr1 . La question qui se pose est la suivante: comment définir un conteneur de fonctions génériques acceptant chacune un nombre variable de parameters (jusqu’à N ~ 5) et constituant une méthode membre différente d’objects ne dérivant pas d’une classe virtuelle commune? Merci

Utilisez std::function pour stocker les appels, puis utilisez un argument de modèle variadique pour transférer et lier les parameters de la fonction:

 class Clock { vector> tasks; template void defer(F f, Args&&... args) { tasks.push_back(bind(f, forward(args)...); } } void f(A a, B b); int main() { A a; B b; Clock c; c.push_back(f, a, b); } 

voir aussi std::bind et std::mem_fun

En C ++ 11, stockez std::function . Vous pouvez utiliser std::bind pour créer la fonction à partir d’une signature différente, par exemple:

 std::vector> deferred; deferred.push_back(std::bind(&Class1::action1, this, arg1, arg2)); // later... for (auto f : deferred) f(); 

Si C ++ 11 n’est pas une option, Boost a des modèles de function et de bind très similaires. Je pense qu’ils existent peut-être aussi dans TR1, bien que je n’ai pas de références historiques à vérifier.

Si Boost n’est vraiment pas une option (et TR1 ne les fournit pas), je vous recommande fortement de le définir. sinon, utilisez Boost comme exemple d’application. Sans modèles variadic, cela devient très poilu.

(Et puisque vous parlez de Modern C ++ Design, lisez la section sur les listes de types; c’est comme cela que vous feriez, sans modèles variadiques).

Puisque vos rappels sont différés, y compris les arguments fournis, la signature réelle sera void() , c’est-à-dire que l’object Clock ne fournira pas d’arguments par lui-même et n’a pas besoin d’évaluer les résultats. Donc généralement, vous voudrez lier les pointeurs de fonction membres (ou d’autres pointeurs de fonction) avec les arguments nécessaires et transmettre le résultat (un object de fonction) à l’horloge. C’est là que boost::bind / std::tr1::bind et boost::function / std::function entrent – ou C ++ 11 lambdas:

 Clock::defer(time, boost::bind(&Class1::action1, this, arg1, arg2)); //C++11 lambda: Clock::defer(time, [=](){action1(arg1, arg2);} ); 

Mais ce que vous faites est déjà fait – jetez un coup d’œil aux timers Boost.Asio:

 boost::asio::basic_deadline_timer timer(ioService, expiryTime); timer.async_wait([=](){action1(arg1, arg2);} //perform action1 when the timer is done