How to pass class member functions to a method in a 3rd party library?

柔情痞子 提交于 2019-12-12 05:23:09

问题


The following is the constructor of a class I would like to use in a 3rd party library (so altering this function isn't an option).

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

However, rather than simply defining 3 global functions, I need each of the functions to know various extra information, which obviously I can't pass as there are no input arguments. To further complicate the issue, I am going to want to make several different instances of this moveset object, each wanting to use the same functions, but upon different underlying data.

My thought is to create a holding class something along these lines,

Class DataPlusFunctions {

 public:

   DataPlusFunctions(Data* dataPtr) { dataPtr_ = dataPtr ;}

   smc::particle<cv_state> 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<cv_state>(value,likelihood(0,value));          

    }

    ... same for other required functions
 private:

  Data* dataPtr_ ;
}

*

Class MainClass {

...
void IK_PFController::initialise() 
{

   std::vector<DataPlusFunctions> dpfV ;

   for (int i = 0 ; i < NSAMPLERS ; i++)
        dpfV.push_back(DataPlusFunctions(&data[i])) ;


  pSamplers_ = (smc::sampler<cv_state>**)(new void* [NSAMPLERS]) ;

  for (int i = 0 ; i < NSAMPLERS ; i++) {

    // Normal way of calling function, having defined global functions e.g.
    //smc::moveset<cv_state> Moveset(fInitialise, fMove, NULL);

    // How to achieve this given my problem ??????????????
    //smc::moveset<cv_state> Moveset(&dpfV[i]::fInitialise, &dpfV[i]::fMove, NULL);

     pSamplers_[i].SetMoveSet(Moveset);

  }

} 

}

Is is allowed? If not, is it possible to achieve what I am attempting given that I will be able to alter the moveset class?


回答1:


In order to call a member function (via pointer), you need an object of the appropriate type. Since the 3rd party function requires vanilla function pointers, you cannot pass a member function.

The best you could do (AFAIK) is to define three functions

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

and set a global variable that those functions access. e.g.:

DataPlusFunctions* g = NULL;

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

and set the value of g before calling the 3rd party function.

The advantage is that you have an object you can use to store state info and also you can replace the pointed-to object with another (maybe even using interface), providing dynamic behavior.

The problem is if you want to use this in a parallel setting as the global might be changed by two threads simultaneously -- in this case you could protect it with a mutex or lock.




回答2:


You can use so-called thunks objects to solve this. The general idea is to generate the functions for which you need pointers at runtime. The famous ATL library on Windows uses this technique. See the article WNDPROC Thunks for some in-depth discussion of this technique including sample code.




回答3:


Since you asked for clarification of my comment, boost::bind allows you to bind a member function pointer to an object (and optionally some number of parameters) to be called later. Here's a simple example:

#include <boost/bind.hpp>
#include <iostream>

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


来源:https://stackoverflow.com/questions/9997299/how-to-pass-class-member-functions-to-a-method-in-a-3rd-party-library

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!