C++ 将指针传递给C++;C API库的成员函数

C++ 将指针传递给C++;C API库的成员函数,c++,c,pointers,callback,C++,C,Pointers,Callback,第一篇文章在这里,我试着看看以前类似的文章,但没有一篇看起来很有效,或者做了我想做的事情 我有一些C代码,称之为library.C。我删除了很多代码并简化了它 //libary.c //接受int的回调函数 无效(*库_cb)(内部); void init(void(*cb())){ //其他代码 库_cb=cb; } 无效样本(){ int数据; //执行一组代码,然后调用回调函数 (*图书馆(数据); } 现在我有C++代码,它定义了我想传递给库中代码的回调函数。 // someclass

第一篇文章在这里,我试着看看以前类似的文章,但没有一篇看起来很有效,或者做了我想做的事情

我有一些C代码,称之为library.C。我删除了很多代码并简化了它

//libary.c
//接受int的回调函数
无效(*库_cb)(内部);
void init(void(*cb())){
//其他代码
库_cb=cb;
}
无效样本(){
int数据;
//执行一组代码,然后调用回调函数
(*图书馆(数据);
}

现在我有C++代码,它定义了我想传递给库中代码的回调函数。

// someclass.cpp

class SomeClass {
    public:
        SomeClass() {

        };

        ~SomeClass() {

        };

        void callback(int data) {
            // Do some stuff
        }
};
然后在main.cpp中,我想做一些

// main.cpp
extern "C" {
    #include "library.h"
}
#include "someclass.h"

SomeClass some_class;

int main() {
    init(&some_class.callback) // obviously doesn't work

    while(true) {
        sample(); // this would call callback in SomeClass
    }
}
现在我知道一个解决方案是将回调定义为

static void callback(int data)
但我想知道是否还有其他方法可以做到这一点。据我所知,std::function或std::mem\u fn可能会有所帮助。但我似乎不知道怎么做

我没有包括头文件,我写了这段代码作为我的问题的一个例子,所以可能有一些语法错误,但希望问题/目标是明确的

编辑:

我应该提到我可以编辑c库

阅读答案后,我似乎可以将c库更改为也接受指向类对象的void*指针,以使其正常工作。有人能给我举个例子吗?我是超级新的C代码与C++接口。

将C++成员函数的指针传递给C API库

。。。这是不可能的

据我所知,std::function或std::mem\u fn可能会有所帮助

这两个都不能用C来调用,但请继续阅读“直到最后”

C只有常规的非成员函数指针,所以这些是C程序可以调用的唯一函数指针。在C++中,这样的指针可以指向自由函数或静态成员函数。

在这种静态或非成员函数的C++实现中,当然可以在C++的权限内做任何事情(尽管让异常逃逸函数会很糟糕),因此您确实可以在那里调用非静态成员函数。p> 但要调用非静态成员函数,需要有一个实例。静态对象是一个简单的解决方案,但不是很灵活,只在少数情况下有用


C语言中设计良好的回调API允许API用户在函数指针之外注册一个通用数据指针(即
void*
),该数据指针将被转发到回调。这种设计允许回调是有状态的——状态存储在指向的对象中。当使用这样的C API时,您可以传递一个指向对象的指针,然后回调可以调用该对象的成员函数。或者,您可以将数据指针传递给
std::function
或其他一些有状态类型的擦除函数包装器,并使用一个泛型自由函数将调用转发给包装器。

您的C API不是很有用。我是这样做的:

  • 回调必须至少采用用户提供的
    void*
    参数,该参数库不会以任何方式进行解释。如果没有这个参数,回调是无用的。是的,它们真的是无用的,你的API用户会因此恨你

  • 如果希望回调函数能够修改其参数的值,可以传递
    void*
    参数的地址。这对于注册时的分配以及在回调执行期间参数更改的类似用途非常有用。这使得库与指针的使用完全解耦:它不仅不解释指针,而且不保持其值不变

  • 库API符号都带有前缀,以防止全局命名空间中发生冲突

  • 根据需要使用typedef以确保代码可读。键入函数指针类型充其量是乏味的

  • 标题应防止多次包含,也就是说,在翻译单元中多次包含标题必须是正确的,没有任何错误

  • 在C++编译单元中编译时,报头声明了一个C接口,因为接口是C语言。C++对符号名称进行加密,头将声明二进制不兼容符号。

  • 标题。这给C++用户带来了优化机会。

  • 考虑库注册多个回调,以及可能在注册和注销时调用回调:这使得与其他编程语言的互操作更加容易

  • H.-可用于C和C++ 下面请注意,私有数据和函数(即不属于API的部分)是这样声明的(
    static

    图书馆.c-实施
    #包括“library.h”
    #包括
    typedef结构回调函数{
    结构回调\u s*下一步;
    库回调函数;
    void*参数;
    }回调;
    静态回调*cb_头;
    void库_init(void){/*其他一些代码*/
    }
    void library_deinit(void){library_deregister_all_callbacks();}
    库\寄存器\结果库\寄存器\回调(库\回调cb,void*cb\参数){
    回调*el=cb_头;
    while(el){
    如果(el->function==cb&&el->parameter==cb\u param)返回库\u REG\u DUPLICATE;
    el=el->next;
    }
    el=malloc(sizeof(回调));
    如果(!el)返回库注册失败;
    el->next=断路器头部;
    el->function=cb;
    el->参数=cb_参数;
    cb_头=el;
    cb(库\寄存器,0,&el->参数);
    返回库注册成功;
    }
    静态int match_回调(常量回调*el,库回调cb,void*cb_参数){
    返回el&&el->function==cb&&el->parameter==cb_param;
    }
    静态int匹配任意回调(常量回调*el,库回调cb,void*cb参数){
    返回el&&el->function==cb;
    }
    静态int匹配所有回调(常量回调*el,库回调cb,voi
    
    #pragma once
    
    #ifdef __cplusplus
    extern "C" {
    #pragma GCC diagnostic push
    // clang erroneously issues a warning in spite of extern "C" linkage
    #pragma GCC diagnostic ignored "-Wc++17-compat-mangling"
    #endif
    
    #ifndef LIBRARY_NOEXCEPT
    #if __cplusplus >= 201103L
    // c.f. https://stackoverflow.com/q/24362616/1329652
    #define LIBRARY_NOEXCEPT noexcept
    #else
    #define LIBRARY_NOEXCEPT
    #endif
    #endif
    
    enum library_register_enum { LIBRARY_REG_FAILURE = 0, LIBRARY_REG_SUCCESS = 1, LIBRARY_REG_DUPLICATE = -1 };
    enum library_call_enum { LIBRARY_SAMPLE, LIBRARY_REGISTER, LIBRARY_DEREGISTER };
    typedef enum library_register_enum library_register_result;
    typedef enum library_call_enum library_call_type;
    #if __cplusplus >= 201103L
    void library_callback_dummy(library_call_type, int, void**) LIBRARY_NOEXCEPT;
    using library_callback = decltype(&library_callback_dummy);
    #else
    typedef void (*library_callback)(library_call_type, int, void**);
    #endif
    
    void library_init(void) LIBRARY_NOEXCEPT;
    library_register_result library_register_callback(library_callback cb, void *cb_param) LIBRARY_NOEXCEPT;
    void library_deregister_callback(library_callback cb, void *cb_param) LIBRARY_NOEXCEPT;
    void library_deregister_any_callback(library_callback cb) LIBRARY_NOEXCEPT;
    void library_deregister_all_callbacks(void) LIBRARY_NOEXCEPT;
    void library_deinit(void) LIBRARY_NOEXCEPT;
    
    void library_sample(void) LIBRARY_NOEXCEPT;
    
    #ifdef __cplusplus
    #pragma GCC diagnostic pop
    }
    #endif
    
    #include "library.h"
    #include <stdlib.h>
    
    typedef struct callback_s {
       struct callback_s *next;
       library_callback function;
       void *parameter;
    } callback;
    
    static callback *cb_head;
    
    void library_init(void) { /* some other code */
    }
    void library_deinit(void) { library_deregister_all_callbacks(); }
    
    library_register_result library_register_callback(library_callback cb, void *cb_param) {
       callback *el = cb_head;
       while (el) {
          if (el->function == cb && el->parameter == cb_param) return LIBRARY_REG_DUPLICATE;
          el = el->next;
       }
       el = malloc(sizeof(callback));
       if (!el) return LIBRARY_REG_FAILURE;
       el->next = cb_head;
       el->function = cb;
       el->parameter = cb_param;
       cb_head = el;
       cb(LIBRARY_REGISTER, 0, &el->parameter);
       return LIBRARY_REG_SUCCESS;
    }
    
    static int match_callback(const callback *el, library_callback cb, void *cb_param) {
       return el && el->function == cb && el->parameter == cb_param;
    }
    
    static int match_any_callback(const callback *el, library_callback cb, void *cb_param) {
       return el && el->function == cb;
    }
    
    static int match_all_callbacks(const callback *el, library_callback cb, void *cb_param) {
       return !!el;
    }
    
    typedef int (*matcher)(const callback *, library_callback, void *);
    
    static void deregister_callback(matcher match, library_callback cb, void *cb_param) {
       callback **p = &cb_head;
       while (*p) {
          callback *el = *p;
          if (match(el, cb, cb_param)) {
             *p = el->next;
             el->function(LIBRARY_DEREGISTER, 0, &el->parameter);
             free(el);
          } else
             p = &el->next;
       }
    }
    
    void library_deregister_callback(library_callback cb, void *cb_param) {
       deregister_callback(match_callback, cb, cb_param);
    }
    
    void library_deregister_any_callback(library_callback cb) {
       deregister_callback(match_any_callback, cb, NULL);
    }
    
    void library_deregister_all_callbacks(void) {
       deregister_callback(match_all_callbacks, NULL, NULL);
    }
    
    void library_sample(void) {
       int data = 42;
       // execute a bunch of code and then call the callback function
       callback *el = cb_head;
       while (el) {
          el->function(LIBRARY_SAMPLE, data, &el->parameter);
          el = el->next;
       }
    }
    
    // https://github.com/KubaO/stackoverflown/tree/master/questions/c-cpp-library-api-53643120
    #include <iostream>
    #include <memory>
    #include <string>
    #include "library.h"
    
    struct Data {
       std::string payload;
       static int counter;
       void print(int value) {
          ++counter;
          std::cout << counter << ": " << value << ", " << payload << std::endl;
       }
    };
    
    int Data::counter;
    
    extern "C" void callback1(library_call_type type, int value, void **param) noexcept {
       if (type == LIBRARY_SAMPLE) {
          auto *data = static_cast<Data *>(*param);
          data->print(value);
       }
    }
    
    using DataPrintFn = std::function<void(int)>;
    
    extern "C" void callback2(library_call_type type, int value, void **param) noexcept {
       assert(param && *param);
       auto *fun = static_cast<DataPrintFn *>(*param);
       if (type == LIBRARY_SAMPLE)
          (*fun)(value);
       else if (type == LIBRARY_DEREGISTER) {
          delete fun;
          *param = nullptr;
       }
    }
    
    void register_callback(Data *data) {
       library_register_callback(&callback1, data);
    }
    
    template <typename F>
    void register_callback(F &&fun) {
       auto f = std::make_unique<DataPrintFn>(std::forward<F>(fun));
       library_deregister_callback(callback2, f.get());
       library_register_callback(callback2, f.release());
       // the callback will retain the functor
    }
    
    int main() {
       Data data;
       data.payload = "payload";
    
       library_init();
       register_callback(&data);
       register_callback([&](int value) noexcept { data.print(value); });
    
       library_sample();
       library_sample();
       library_deinit();  // must happen before the 'data' is destructed
       assert(data.counter == 4);
    }