Comment passer des fonctions de membre de classe à une méthode dans une bibliothèque tierce?

Ce qui suit est le constructeur d’une classe que j’aimerais utiliser dans une bibliothèque tierce (donc, modifier cette fonction n’est pas une option).

template  moveset::moveset(particle (*pfInit)(rng*), void (*pfNewMoves)(long, particle &,rng*), int (*pfNewMCMC)(long,particle &,rng*)) 

Cependant, plutôt que de simplement définir 3 fonctions globales, j’ai besoin que chacune des fonctions connaisse diverses informations supplémentaires, ce que je ne peux évidemment pas transmettre car il n’y a pas d’argument d’entrée. Pour compliquer davantage le problème, je vais créer plusieurs instances différentes de cet object moveet, chacune souhaitant utiliser les mêmes fonctions, mais sur des données sous-jacentes différentes.

Ma pensée est de créer une classe de maintien quelque chose dans ce sens,

 Class DataPlusFunctions { public: DataPlusFunctions(Data* dataPtr) { dataPtr_ = dataPtr ;} smc::particle fInitialise(smc::rng *pRng) { // the actual function will be a lot more complicated than this and // likely to require calling other methods / classes. // The Data stored in a different class will be changing...which is // important in relation to the pfNewMoves function. double value = dataPtr_->value() ; return smc::particle(value,likelihood(0,value)); } ... same for other required functions private: Data* dataPtr_ ; } 

*

 Class MainClass { ... void IK_PFController::initialise() { std::vector dpfV ; for (int i = 0 ; i < NSAMPLERS ; i++) dpfV.push_back(DataPlusFunctions(&data[i])) ; pSamplers_ = (smc::sampler**)(new void* [NSAMPLERS]) ; for (int i = 0 ; i < NSAMPLERS ; i++) { // Normal way of calling function, having defined global functions eg //smc::moveset Moveset(fInitialise, fMove, NULL); // How to achieve this given my problem ?????????????? //smc::moveset Moveset(&dpfV[i]::fInitialise, &dpfV[i]::fMove, NULL); pSamplers_[i].SetMoveSet(Moveset); } } } 

Est-ce permis? Sinon, est-il possible de réaliser ce que je tente, étant donné que je pourrai modifier la classe de moveets?

Pour appeler une fonction membre (via un pointeur), vous avez besoin d’un object du type approprié. Étant donné que la fonction tierce requirejs des pointeurs de fonction vanille, vous ne pouvez pas transmettre de fonction membre.

Le mieux que vous puissiez faire (autant que je sache) est de définir trois fonctions

 particle Init(rng*); void NewMoves(long, particle &,rng*); int NewMCMC(long,particle &,rng*); 

et définir une variable globale à laquelle ces fonctions accèdent. par exemple:

 DataPlusFunctions* g = NULL; particle Init(rng* r) { // g==NULL handling omitted return g->fInitialise(r); } // similarly for the others 

et définissez la valeur de g avant d’appeler la fonction tierce.

L’avantage est que vous disposez d’un object que vous pouvez utiliser pour stocker des informations d’état et que vous pouvez également remplacer l’object pointé par un autre object (peut-être même en utilisant une interface), offrant ainsi un comportement dynamic.

Le problème est que si vous voulez utiliser cela dans un paramètre parallèle car le global peut être modifié par deux threads simultanément – dans ce cas, vous pouvez le protéger avec un mutex ou un verrou.

Vous pouvez utiliser des objects dits thunks pour résoudre ce problème. L’idée générale est de générer les fonctions pour lesquelles vous avez besoin de pointeurs lors de l’exécution. La célèbre bibliothèque ATL sur Windows utilise cette technique. Voir l’article WNDPROC Thunks pour une discussion approfondie de cette technique, y compris un exemple de code.

Puisque vous avez demandé des éclaircissements sur mon commentaire, boost::bind vous permet de lier un pointeur de fonction membre à un object (et éventuellement un certain nombre de parameters) à appeler ultérieurement. Voici un exemple simple:

 #include  #include  class Hello { public: void World() { std::cout << "Hello World.\n"; } }; class SomethingElse { public: void Grumble(int x) { std::cout << x << " Grumble, Grumble...\n"; } }; int main() { Hello obj; // bind obj.World() to a functor that can be called later auto f = boost::bind(&Hello::World, &obj); // example... f(); SomethingElse obj2; // bind obj2.Grumble(13) to a functor that can be called later auto g = boost::bind(&SomethingElse::Grumble, obj2, 13); // example... g(); }