English 中文(简体)
C++: static function wrapper that routes to member function?
原标题:

I ve tried all sorts of design approaches to solve this problem, but I just can t seem to get it right.

I need to expose some static functions to use as callback function to a C lib. However, I want the actual implementation to be non-static, so I can use virtual functions and reuse code in a base class. Such as:

class Callbacks {
  static void MyCallBack() { impl->MyCallBackImpl(); }
  ...

class CallbackImplBase {
   virtual void MyCallBackImpl() = 0;

However I try to solve this (Singleton, composition by letting Callbacks be contained in the implementor class, etc) I end up in a dead-end (impl usually ends up pointing to the base class, not the derived one).

I wonder if it is at all possible or if I m stuck with creating some sort of helper functions instead of using inheritance?

最佳回答

Are any of the parameters passed to the callback function user defined? Is there any way you can attach a user defined value to data passed to these callbacks? I remember when I implemented a wrapper library for Win32 windows I used SetWindowLong() to attach a this pointer to the window handle which could be later retrieved in the callback function. Basically, you need to pack the this pointer somewhere so that you can retrieve it when the callback gets fired.

struct CALLBACKDATA
{
  int field0;
  int field1;
  int field2;
};

struct MYCALLBACKDATA : public CALLBACKDATA
{
  Callback* ptr;
};


registerCallback( Callback::StaticCallbackFunc, &myCallbackData, ... );

void Callback::StaticCallbackFunc( CALLBACKDATA* pData )
{
  MYCALLBACKDATA* pMyData = (MYCALLBACKDATA*)pData;
  Callback* pCallback = pMyData->ptr;

  pCallback->virtualFunctionCall();
}
问题回答

Problem 1:

Though it may look and seem to work on your setup this is not guaranteed to work as the C++ ABI is not defined. So technically you can not use C++ static member functions as functions pointers to be used by C code.

Problem 2:

All C callacks (that I know of) allow you to pass user data back as a void*. You can use this as the pointer to your object that has the virtual method. BUT You must make sure you use dynamic_cast<>() to the base class (the one with the virtual method used in the callback) before it is converted into the void* otherwise the pointer at the other end may not be interpreted correctly (especially if there is multiple inheritance involved).

Problem 3:

Exceptions: C is not designed to work with exceptions (especially old C libraries with callbacks). So don t expect exceptions that escape your callback to provide anything meaningful to the caller (they are more likely to result in application termination).

Solution:

What you need to do is use extern "C" function as the callback that calls the virtual method on an object of know type and throws away all exceptions.

An example for the C pthread routines

#include <iostream>

extern "C" void* start_thread(void* data);

class Work
{
    public:
    virtual ~Work() {}
    virtual void doWork() = 0;
};

/*
 * To be used as a callback for C code this MUST be declared as
 * with extern "C" linkage to make sure the calling code can
 * correctly call it
 */
void* start_thread(void* data)
{
    /*
     * Use reinterpret_cast<>() because the only thing you know
     * that you can do is cast back to a Work* pointer.
     *
     */
    Work*  work = reinterpret_cast<Work*>(data);
    try
    {
        work->doWork();
    }
    catch(...)
    {
        // Never let an exception escape a callback.
        // As you are being called back from C code this would probably result
        // in program termination as the C ABI does not know how to cope with
        // exceptions and thus would not be able to unwind the call stack.
        //
        // An exception is if the C code had been built with a C++ compiler
        // But if like pthread this is an existing C lib you are unlikely to get
        // the results you expect.
    }
    return NULL;
}

class PrintWork: public Work
{
    public:
    virtual void doWork()
    {
        std::cout << "Hi 
";
    }
};

int main()
{
    pthread_t   thread;
    PrintWork   printer;
    /*
     * Use dynamic_cast<>() here because you must make sure that
     * the underlying routine receives a Work* pointer
     * 
     * As it is working with a void* there is no way for the compiler
     * to do this intrinsically so you must do it manually at this end
     */
    int check = pthread_create(&thread,NULL,start_thread,dynamic_cast<Work*>(&printer));
    if (check == 0)
    {
        void*   result;
        pthread_join(thread,&result);
    }
}


    

It s possible. Perhaps there s a problem on how you re initializing the concrete implementation?

In fact, I remember one library that does something very similar to this. You might find it usefull to take a look at libxml++ source code. It s built on top of libxml, which is a C library.

libxml++ uses a struct of static functions to handle the callbacks. For customization, the design allows the user to provide (through virtual functions) his/her own implementations to which the callbacks are then forwarded. I guess this is pretty much your situation.

Something like the below. The singleton is in class Callback, the Instance member will return a statically allocated reference to a CallbackImpl class. This is a singleton because the reference will only be initialised once when the function is first called. Also, it must be a reference or a pointer otherwise the virtual function will not work.

class CallbackImplBase
{
public:
   virtual void MyCallBackImpl() = 0;
};

class CallbackImpl : public CallbackImplBase
{
public:
    void MyCallBackImpl()
    {
        std::cout << "MyCallBackImpl" << std::endl;
    }
};

class Callback
{
public:
    static CallbackImplBase & Instance()
    {
        static CallbackImpl instance;
        return instance;
    }

    static void MyCallBack()
    {
        Instance().MyCallBackImpl();
    }
};

extern "C" void MyCallBack()
{
    Callback::MyCallBack();
}




相关问题
Undefined reference

I m getting this linker error. I know a way around it, but it s bugging me because another part of the project s linking fine and it s designed almost identically. First, I have namespace LCD. Then I ...

C++ Equivalent of Tidy

Is there an equivalent to tidy for HTML code for C++? I have searched on the internet, but I find nothing but C++ wrappers for tidy, etc... I think the keyword tidy is what has me hung up. I am ...

Template Classes in C++ ... a required skill set?

I m new to C++ and am wondering how much time I should invest in learning how to implement template classes. Are they widely used in industry, or is this something I should move through quickly?

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

typedef ing STL wstring

Why is it when i do the following i get errors when relating to with wchar_t? namespace Foo { typedef std::wstring String; } Now i declare all my strings as Foo::String through out the program, ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

Window iconification status via Xlib

Is it possible to check with the means of pure X11/Xlib only whether the given window is iconified/minimized, and, if it is, how?

热门标签