背景:近期接手的项目中,需要将原来 C++ 中的一个底层接口替换为作者所写的 C,其中希望有一处地方希望 C++ 给 C 传递一个回调函数,以触发条件的时候调用该函数
(本人 C++ 小白一枚,仅以此篇记录遇到的问题并分享给各位,如有问题欢迎评论指正)
C++ 向 C 传递 回调函数
前提
实验环境:
- ubuntu20.04
- gcc version 9.4.0
根据背景中提到的问题,我将该部分简化为一个 demo, 主要包含 c_test.h c_test.c c++_test.cpp 三个文件
除此之外还有一个 Makefile,因为几个方法的 Makefile 都相同,故在此展示
CC = gcc
CXX = g++
TARGET = test
AllDIRS := $(shell ls -R | grep '^\./.*:$$' | awk '{gsub(":","");print}') .
CSRCS = $(foreach n,$(AllDIRS) , $(wildcard $(n)/*.c))
CXXSRCS = $(foreach n,$(AllDIRS) , $(wildcard $(n)/*.cpp))
OBJS := $(CSRCS:.c=.o) $(CXXSRCS:.cpp=.o)
SRC_PATH = ./
INC_PATH = -I $(SRC_PATH)
#compile
%.o : %.c
$(CC) $(INC_PATH) -c $< -o $@ -g
%.o : %.cpp
$(CXX) $(INC_PATH) $< -c -o $@ -g
#links
$(TARGET) : $(OBJS)
$(CXX) $^ -g -o $@ $(INC_PATH)
@rm -rf $(OBJS)
@echo "build success"
.PHONY:clean
clean:
@echo "Remove linked and compiled files......"
rm -rf $(OBJS) $(TARGET)
静态函数
使用静态函数,该方法是最为简单的,跟 C 中没有太多区别,直接上代码
c_test.h
#ifndef C_TEST_H_
#define C_TEST_H_
#ifdef __cplusplus
extern "C"
{
#endif
typedef void (*Callback)(int);
typedef struct c_test {
Callback callback;
}c_test_t;
void c_run(Callback fun);
#ifdef __cplusplus
}
#endif
#endif
c_test.c
#include <stdio.h>
#include "c_test.h"
void c_run(Callback fun){
c_test_t c_test;
c_test.callback = fun;
c_test.callback(2);
}
c++_test.c
#include "c_test.h"
#include <stdio.h>
#include <iostream>
#include <functional>
using namespace std;
namespace test_interface {
class A {
public:
A(void);
private:
static int a_private_var;
static void fn(int i) {
test_interface::A::a_private_var = 1;
printf("%d\n", test_interface::A::a_private_var);
printf("%d\n", i);
}
};
}
int test_interface::A::a_private_var;
namespace test_interface {
A::A(void) {
c_run(A::fn);
}
}
int main(int argc, char* argv[]) {
test_interface::A a;
return 0;
}
通过代码可以看见,该方法的缺陷在于 C++传递的 callback 函数必须是静态函数,这样内部使用的变量也就只能是静态变量了,属于整个类了,而不属于某个对象,这是很糟糕的,但不得不说该方法确实 easy
定义新函数
在 C++ 中定义一个 Callback类型的函数,在该函数中声明类对象,将该函数传递给 C
该方法中仅 c++_test.c 文件不一样
c++_test.c
#include "c_test.h"
#include <stdio.h>
#include <iostream>
#include <functional>
using namespace std;
namespace test_interface {
class A {
public:
void fn2(int i) { fn(i); }
private:
int a_private_var;
void fn(int i) {
test_interface::A::a_private_var = 1;
printf("%d\n", test_interface::A::a_private_var);
printf("%d\n", i);
}
};
}
void new_func(int n) {
test_interface::A a;
a.fn2(n);
}
int main(int argc, char* argv[]) {
c_run(&new_func);
return 0;
}
lambda方法
来到重头戏了,前两种方法在我项目中都不适用,所以找到了这篇博客
其对通过 lambda 向 C 传递给函数指针回调作了多种实验以及测试,代码详情可参考代码地址
对于 lambda 不是很了解的可以参考
lambda概念与用法
lambda概念与用法
本篇主要参考了示例5 ,使用*void 上下文指针将捕获 lambda 传递给 C 函数指针回调
c_test.h
#ifndef C_TEST_H_
#define C_TEST_H_
#ifdef __cplusplus
extern "C"
{
#endif
typedef void (*Callback)(int, void* context);
typedef struct c_test {
Callback callback;
void *callback_context;
}c_test_t;
void c_run(Callback fun, void* context);
#ifdef __cplusplus
}
#endif
#endif
c_test.c
#include <stdio.h>
#include "c_test.h"
void c_run(Callback fun, void* context){
c_test_t c_test;
c_test.callback = fun;
c_test.callback_context = context;
c_test.callback(2, c_test.callback_context);
}
c++_test.c
#include "c_test.h"
#include <stdio.h>
#include <iostream>
#include <functional>
using namespace std;
namespace test_interface {
class A {
public:
A(void);
private:
int a_private_var;
void fn(int i) {
A::a_private_var = 1;
printf("%d\n", A::a_private_var);
printf("%d\n", i);
}
};
}
namespace test_interface {
using FunctionCallback = std::function<void (int data)>;
A::A(void) {
auto adpter_for_lambda = [](int n, void *context) {
FunctionCallback* pFunc = reinterpret_cast<FunctionCallback*>(context);
(*pFunc)(n);
};
auto callback_lambda = [this](int n) { A::fn(n); };
FunctionCallback callback_object = callback_lambda;
c_run(adpter_for_lambda, &callback_object);
}
}
int main(int argc, char* argv[]) {
test_interface::A a;
return 0;
}
该例是通过 C 提供一个额外的空指针,用来指向函数指针的上下文或状态
其他
另外一篇参考博客如何将C++λ传递给需要函数指针和上下文的C回调?