Convert C++ function pointer to c function pointer

后端 未结 7 1082
无人共我
无人共我 2020-12-01 04:42

I am developing a C++ application using a C library. I have to send a pointer to function to the C library.

This is my class:

 class MainWindow : pub         


        
相关标签:
7条回答
  • 2020-12-01 05:10

    @Snps answer is perfect! But as @DXM mentioned it can hold only one callback. I've improved it a little, now it can keep many callbacks of the same type. It's a little bit strange, but works perfect:

     #include <type_traits>
    
    template<typename T>
    struct ActualType {
        typedef T type;
    };
    template<typename T>
    struct ActualType<T*> {
        typedef typename ActualType<T>::type type;
    };
    
    template<typename T, unsigned int n,typename CallerType>
    struct Callback;
    
    template<typename Ret, typename ... Params, unsigned int n,typename CallerType>
    struct Callback<Ret(Params...), n,CallerType> {
        typedef Ret (*ret_cb)(Params...);
        template<typename ... Args>
        static Ret callback(Args ... args) {
            func(args...);
        }
    
        static ret_cb getCallback(std::function<Ret(Params...)> fn) {
            func = fn;
            return static_cast<ret_cb>(Callback<Ret(Params...), n,CallerType>::callback);
        }
    
        static std::function<Ret(Params...)> func;
    
    };
    
    template<typename Ret, typename ... Params, unsigned int n,typename CallerType>
    std::function<Ret(Params...)> Callback<Ret(Params...), n,CallerType>::func;
    
    #define GETCB(ptrtype,callertype) Callback<ActualType<ptrtype>::type,__COUNTER__,callertype>::getCallback
    

    Now you can just do something like this:

    typedef void (cb_type)(uint8_t, uint8_t);
    class testfunc {
    public:
        void test(int x) {
            std::cout << "in testfunc.test " <<x<< std::endl;
        }
    
        void test1(int x) {
            std::cout << "in testfunc.test1 " <<x<< std::endl;
        }
    
    };
    
    cb_type* f = GETCB(cb_type, testfunc)(std::bind(&testfunc::test, tf, std::placeholders::_2));
    
    cb_type* f1 = GETCB(cb_type, testfunc)(
                    std::bind(&testfunc::test1, tf, std::placeholders::_2));
    
    
    f(5, 4);
    f1(5, 7);
    
    0 讨论(0)
  • 2020-12-01 05:16

    If I recall it correctly, Only static methods of a class can be accessed via "normal" C pointer to function syntax. So try to make it static. The pointer to a method of a class needs extra information, such as the "object" (this) which has no meaning for a pure C method.

    The FAQ shown here has good explanation and a possible (ugly) solution for your problem.

    0 讨论(0)
  • 2020-12-01 05:21

    You can't pass a function pointer to a non-static member function. What you can do is to create a static or global function that makes the call with an instance parameter.

    Here's an example I find useful which uses a helper class with two members: a function wrapper and a callback function that calls the wrapper.

    template <typename T>
    struct Callback;
    
    template <typename Ret, typename... Params>
    struct Callback<Ret(Params...)> {
        template <typename... Args>
        static Ret callback(Args... args) { return func(args...); }
        static std::function<Ret(Params...)> func;
    };
    
    // Initialize the static member.
    template <typename Ret, typename... Params>
    std::function<Ret(Params...)> Callback<Ret(Params...)>::func;
    

    Using this you can store any callable, even non-static member functions (using std::bind) and convert to a c-pointer using the Callback::callback function. E.g:

    struct Foo {
        void print(int* x) { // Some member function.
            std::cout << *x << std::endl;
        }
    };
    
    int main() {
        Foo foo; // Create instance of Foo.
    
        // Store member function and the instance using std::bind.
        Callback<void(int*)>::func = std::bind(&Foo::print, foo, std::placeholders::_1);
    
        // Convert callback-function to c-pointer.
        void (*c_func)(int*) = static_cast<decltype(c_func)>(Callback<void(int*)>::callback);
    
        // Use in any way you wish.
        std::unique_ptr<int> iptr{new int(5)};
        c_func(iptr.get());
    }
    
    0 讨论(0)
  • 2020-12-01 05:21

    I've got an idea (not entirely standard-compliant, as extern "C" is missing):

    class MainWindow;
    
    static MainWindow* instance;
    
    class MainWindow
    {
    public:
      MainWindow()
      {
        instance = this;
    
        registerCallback([](int* arg){instance->...});
      }
    };
    

    You will have problems if multiple instances of MainWindow are instantiated.

    0 讨论(0)
  • 2020-12-01 05:22

    @Snps answer is great. I extended it with a maker function that creates a callback, as I always use void callbacks without parameters:

    typedef void (*voidCCallback)();
    template<typename T>
    voidCCallback makeCCallback(void (T::*method)(),T* r){
      Callback<void()>::func = std::bind(method, r);
      void (*c_function_pointer)() = static_cast<decltype(c_function_pointer)>(Callback<void()>::callback);
      return c_function_pointer;
    }
    

    From then on, you can create your plain C callback from within the class or anywhere else and have the member called:

    voidCCallback callback = makeCCallback(&Foo::print, this);
    plainOldCFunction(callback);
    
    0 讨论(0)
  • 2020-12-01 05:24

    The short answer is: you can convert a member function pointer to an ordinary C function pointer using std::mem_fn.

    That is the answer to the question as given, but this question seems to have a confused premise, as the asker expects C code to be able to call an instance method of MainWindow without having a MainWindow*, which is simply impossible.

    If you use mem_fn to cast MainWindow::on_btn_clicked to a C function pointer, then you still a function that takes a MainWindow* as its first argument.

    void (*window_callback)(MainWindow*,int*) = std::mem_fn(&MainWindow::on_btn_clicked);
    

    That is the answer to the question as given, but it doesn't match the interface. You would have to write a C function to wrap the call to a specific instance (after all, your C API code knows nothing about MainWindow or any specific instance of it):

    void window_button_click_wrapper(int* arg)
    {
        MainWindow::inst()->on_btn_clicked(arg);
    }
    

    This is considered an OO anti-pattern, but since the C API knows nothing about your object, it's the only way.

    0 讨论(0)
提交回复
热议问题