0

I'm working with a particle simulation library. The way interactions are added to particles it through the following library function:

AddInteraction(ParticleSet particleSet, void(*interaction)(xyz* p, xyz* v))

I now want to pass a member function to AddInteraction. I've understood that this is impossible to do without changing the library function. Changing the library is something I'd like to avoid, but if the change is small I can mail the author of the library and ask for it to be implemented.

I want to write a simple example of how it can be done. Different solutions are possible:

  • Lambda expressions. These would be perfect, however the library uses CUDA and NVCC, which don't support Lamda expressions yet.

  • Functors. I thought functors could do the job. However, I haven't managed to get them working in the way I want. For example, I can't get a list of functors to work. This problem is described in http://www.tutok.sk/fastgl/callback.html under Parameterize the Caller:

' If a component has many callback relationships it quickly becomes unworkable to parameterize them all. Consider a Button that wants to maintain a dynamic list of callees to be notified upon a click event. Since the callee type is built into the Button class type, this list must be either homogeneous or typeless.'

I have to admit that I don't understand everything that is written on that site, so there might be an answer there.

  • Using a 3d party library. For example the 'Callbacks Using Template Functors Library' as described on http://www.tutok.sk/fastgl/callback.html, or using Boost::Function. However, these will probably require major changes to the particle library I'm working with, right?

Are there any alternatives? Or is there a way to get one of the above solutions to work?

Thanks a lot!

Edit:

Thanks for your suggestions. They help, but I fail to see how they give a complete solution to my problem. Specifically, I'm trying to get this to work:

std::vector<void(*)(xyz*,xyz*)> interactionList;

void AddInteraction(void(*func)(xyz*,xyz*))
{
    interactionList.push_back(func);
}

void Simulate()
{
    for(size_t i = 0; i < interactionList.size(); i++)
    {
        interactionList[i](0,0); //Would normally be called for every particle
    }
}

class Cell {
public:
    void UpdateParticle(xyz* p, xyz* v);
    Cell()
    {
        AddInteraction(this->UpdateParticle); //Does not work
    }
};

int main()
{
    Cell cell1;
    Cell cell2;

    for(int i = 0; i < 100; i++)
    {
        Simulate();
    }

    return 1;
}
DnDiene
  • 13
  • 1
  • 7

3 Answers3

7

It is possible to make that library function call a member function without modifying the library. The technique (called thunking) is quite general and can be thought of as a sort of just-in-time (JIT) compilation. It is employed by some widely used libraries (like ATL and WTL on Windows).

Basically you want to create a copy of a function like this:

void callback(xyz* p, xyz* v) {
    YourClass *ptr = (YourClass*)0xABCDEF00; // this pointer
    ptr->member_callback(p, v);
}

for each instance of YourClass. Then you can pass it to the library as usual:

AddInteraction(particleSet, this->a_pointer_to_an_instance_of_that_function);

This approach is simple once you're familiar with machine code, but it's not portable (yet can be implemented on any reasonable architecture).

For details see the description and implementation for Windows.

EDIT: Here is how one can generate the machine code for the thunk without actually knowing assembly. Compile this code in release:

void f(int *a, int *b)
{
    X *ptr = reinterpret_cast<X*>(0xAAAAAAAA);
    void (*fp)(void*, int*, int*) = reinterpret_cast<void(*)(void*, int*, int*)>(0xBBBBBBBB);
    fp(ptr, a, b);
}

int main()
{
    void (*volatile fp)(int*, int*) = f;
    fp(0, 0);
}

Put a breakpoint in f and run. Look at the assembly code in the debugger. On my machine it looks like this:

00401000   8B 44 24 08      mov         eax,dword ptr [esp+8] 
00401004   8B 4C 24 04      mov         ecx,dword ptr [esp+4] 
00401008   50               push        eax  
00401009   51               push        ecx  
0040100A   68 AA AA AA AA   push        0AAAAAAAAh 
0040100F   BA BB BB BB BB   mov         edx,0BBBBBBBBh 
00401014   FF D2            call        edx  
00401016   83 C4 0C         add         esp,0Ch 
00401019   C3               ret 

The first column is the memory address of the code, the second is the machine code (you may need to enable it in MSVC by right clicking > show code bytes), the third is the disassembled code. What we need is the second column. You can just copy it from here or use any other method (like object file listing).

This code corresponds to a function with a default calling convention, receives two pointers (the type doesn't matter here), returns nothing, and performs a call to a function at address 0xBBBBBBBB passing 0xAAAAAAAA as the first parameter. Voilà! This is exactly how our thunk should look like!

Initialize the thunk with the machine code from above:

8B 44 24 08 8B 4C 24 04 50 51 68 AA AA AA AA BA BB BB BB BB FF 83 C4 0C C3

And replace AA AA AA AA with this address and BB BB BB BB with a pointer to the following function. To avoid endianess issues use unaligned access.

void delegate(YourClass *that, xyz p, xyz* v) {
    that->member(p, v);
}

We magically encoded that and f inside a single function pointer! And since the last call is likely to be inlined, the whole thing costs just one more function call compared to the void* approach.

⚠ WARNING ⚠ There are some restrictions on what you can write in f above. Any code that will compile to machine code with relative addressing will not work. But the above is just enough for accomplishing the task.

NOTE: It's possible to call the member function directly without the delegate function, as done in the CodeProject article. However, due to the complexities of what member function pointers are, I prefer to do not do this.

NOTE: The code generate by this method is suboptimal. The optimal code equivalent to the above would be:

00401026 68 AA AA AA AA   push        0AAAAAAAAh 
0040102B BA BB BB BB BB   mov         edx,0BBBBBBBBh 
00401030 FF E2            jmp         edx  
Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • This seems like exactly the thing I need. However, the pc I'm working on is 64-bit, and according to [this](http://www.codefornuts.com/2007/05/thunking-in-x64-one-assembly-approach.html) article thunking is a lot more complex on 64-bit architecture. I there any option besides switching to a 32 bit computer? – DnDiene Jul 17 '12 at 11:59
  • Nevermind that, Visual Studio compiles to Win32 anyway. I've got the Thunk32 library running now. Will let you know if it solves my problem, but it seems promising. Thanks a lot! – DnDiene Jul 17 '12 at 12:56
  • Almost there. Using the Thunk32 library I can get a void(__stdcall *)(xyz*,xyz*) from my member function. However, this is incompatible with the void(*)(xyz*,xyz*) the particle library need. Any idea how I can convert one to another? By the way the library also accepts void(__cdecl*)(xyz*,xyz*) – DnDiene Jul 17 '12 at 13:30
  • Yeah, it's right. The AMD64 calling convention is a bit harder, since it passes some of the stuff in registers. This would mean that you'll need a different thunk code for 0 to 4 parameters (above 4 it doesn't matter). Also you can convert anything to anything by using an auxiliary global function. Unfortunately you'll need to understand a bit of assembly and calling conventions to accomplish this. I'll see if I have time to write more detailed description till the end of this week. – Yakov Galka Jul 17 '12 at 13:56
  • Thanks for your explaination. I know virtually nothing about assembly and calling conventions, so writing the thunk method from scratch will take a lot of time. I haven't got much time left to work on this project, so I've got to find some library or other quick solution. How would the AddInteraction function have to be written to make all this unneccecary? – DnDiene Jul 17 '12 at 14:16
  • @user1526712: A C++ way is `std::function` instead of a function pointer. Typically such library callbacks let you pass an additional user data in a `void*` parameter, as others described, so `AddInteraction` would become `AddInteraction(ParticleSet particleSet, void(*interaction)(void* userdata, xyz* p, xyz* v), void *userdata)`. Then the user (you) writes a global function that casts `userdata` to `YourClass*` and calls the desired member. – Yakov Galka Jul 17 '12 at 14:56
  • Thank you very much. I've got an example written out using void* userdata. I'll email the author and try to get it implemented. – DnDiene Jul 18 '12 at 11:35
1

What you describe is not possible because the library does not know it has to pass this parameter to your member functions. You could do it if interaction accepted an argument reserved for the user. If you have a single object instance calling AddInteraction at any given time, then you can store a pointer to the instance:

Object *Object::only_instance;

void Object::AddInteractionCaller() {
   only_instance = this;
   AddInteraction(set, interaction_fn); 
}

void interaction_fn(xyz* p, xyz* v) {
  only_instance->interaction(p, v);
}
perreal
  • 94,503
  • 21
  • 155
  • 181
  • Please note that you _can_ pass a member function pointer if the particle library is written in C++ and you define your member function as static. This way you get a class specific callback functionality, but not an object specific one. – boto Jul 16 '12 at 15:47
  • How would this be used to get the code I posted in the edit to work? – DnDiene Jul 17 '12 at 14:36
  • where is 3rd party in the example? – perreal Jul 17 '12 at 14:41
  • In the example I am the one writing the 'library', because I want to give the library author an example of how it can be done. – DnDiene Jul 17 '12 at 15:51
0

Typically, callback function have a void * argument that allows client code a placeholder for any other information it may require.

This way, the client code can pass in anything they want and recast it back to the original type when the callback is invoked. Note, the calling code knows the original type.

This type of interface allows C only code to work but it's pretty easy to wrap this in a C++ object if needed. At the very least, the library author should provide this.

EDIT TO ANSWER OPs COMMENT
Below I've modified the Cell class appropriately.

class Cell  
{ 
public:
  static void UpdateParticle(void *stuff, xyz* p, xyz* v)  // this has to be static as others have mentioned, note the additional void * argument
  {
      Cell * c = (Cell *) stuff;
      // do work
  }  

  Cell()
  {
      AddInteraction(this, UpdateParticle); // note this takes a two items, both of which have to be saved for future use by AddInteraction and utilized by Simulate
  }
};  
Anon Mail
  • 4,660
  • 1
  • 18
  • 21
  • Could you type me a quick example of how you would use this to get the code I've posted in the edit above to work? – DnDiene Jul 17 '12 at 14:37
  • Of course, if you're the one writing the library, I would not implement it this way. This is how C libraries implement callbacks. For C++ libraries, it's better to use functors objects. – Anon Mail Jul 17 '12 at 17:16
  • Finally I understand how it works. Thanks! I'll email the author and try to get it implemented. – DnDiene Jul 18 '12 at 11:36