tags:

views:

103

answers:

2

I'm wrapping an existing C API to make it easier to use in my VS2008 C++ program. The C API is expecting an array of "TABLE_ENTRY" structures that include a function pointer as in the code below.

But, I'm having difficulty storing a pointer to a member function in the function pointer. Can anybody point out what I may be doing wrong?

Thanks, PaulH

My code looks basically like this:

struct TABLE_ENTRY; // forward decl

typedef int (WINAPI *MYPROC )(DWORD msg, TABLE_ENTRY* entry);

struct TABLE_ENTRY {
    const char* description;
    DWORD value;
    MYPROC callback;
};

class MyClass
{
public:
    MyClass() : description( "Some Description" ),
                some_value( 1 )
    {
    };

    int MyProc( DWORD msg, TABLE_ENTRY* my_entry )
    {
        return 0;
    };

    TABLE_ENTRY* operator*()
    {
        entry_.description = description.c_str();
        entry_.value = some_value;
        // error C2440: '=' : cannot convert from 'boost::_bi::bind_t<R,F,L>' to 'MYPROC'
        entry_.callback = boost::bind< int >( &MyClass::MyProc, this );
        return &entry_;
    };

    TABLE_ENTRY entry_;
    std::string description;
    DWORD some_value;
};

class MyClassCollection
{
public:
    TABLE_ENTRY* GetTable()
    {
        // is this okay or is it Evil & wrong?
        return ( &collection_.front() )->operator*();
    };

    void Add( MyClass& my_class )
    {
        collection_.push_back( my_class );
    }
private:
    std::vector< MyClass > collection_;
};

int _tmain( int argc, _TCHAR* argv[] )
{
    MyClass class1;
    MyClass class2;

    MyClassCollection collection;
    collection.Add( class1 );
    collection.Add( class2 );

    TABLE_ENTRY* table = collection.GetTable();
    TABLE_ENTRY entry1 = table[ 0 ]; // should be class1's table
    TABLE_ENTRY entry2 = table[ 1 ]; // should be class2's table

    return 0;
}
+2  A: 

boost::bind creates a functor, i.e. an instance of a class that implements operator(). This is not interchangeable with plain C function pointers.

Nikolai N Fetissov
The solution being don't use `boost::bind`. You have use a vanilla function pointer, not even a member function pointer (which requires an instance of a class to call on.) This is an unfortunate thing of statically typed languages, especially C (which the API is), where we can't even use templates to at least get a bit more generality (such a with `boost::bind`). You'll have to make a static function that somehow delegates the call to the "real" procedure.
GMan
The question is, however, how to bind the `this` pointer. The static functon pointer has no way to know on what object to invoke on. I bet some of the fields in that entry can serve for this, tho :)
Johannes Schaub - litb
Thanks, @GMan. Pretty much what I was about to add.
Nikolai N Fetissov
Most C callback APIs have a notion of *closure* or *private data* to get back at, say, struct for specific device in a device driver. I wonder what that `description` field is for ...
Nikolai N Fetissov
One dirty hack is to `reinterpret_cast` the address of the callback `entry` parameter to address of containing class. **disclaimer:** table entry must be the first member of the class; the class must not be polymorphic or even derived from anything; this might not work on your particular compiler; don't try it at home, etc.
Nikolai N Fetissov
@litb: Indeed, a very vague "somehow" in my original comment. :P Nikolai is right, most if not all Win32 callbacks provide some way of getting unique data from it. This usually makes for a good key into a `map` (or even better, `unordered_map`) to find the corresponding object. Then just forward the parameters into the actual function.
GMan
@litb - You're correct. The API does include a parameter I can use to pass a "this" pointer and end up with the functionality I wanted.My operator*() is broken, but I'll start a new question for that one.Thanks!
PaulH
+1  A: 

This:

typedef int (WINAPI *MYPROC )(DWORD msg, TABLE_ENTRY* entry);

...needs to be this:

typedef int (WINAPI MyClass::*MYPROC )(DWORD msg, TABLE_ENTRY* entry);

There were a whole bunch of other problems, but I think what you were asking about was your member function pointer, so I don't need to comment on the rest.

John Dibling
Well, what the OP obviously wants is to convert former into the latter by "binding" the `this` parameter.
AndreyT
@AndreyT: You might be right, but that's not how I read the OP. I gathered from the title of the post that the problem was storing a pointer to a member function, and PaulH was simply using bind to accomplish that goal. Not a question about bind so much as a question about member function pointers. But again, you could be right.
John Dibling