将 C++ 类放入 C-struct 以供 C++ 函数使用(C/C++ 混合代码)
Posted
技术标签:
【中文标题】将 C++ 类放入 C-struct 以供 C++ 函数使用(C/C++ 混合代码)【英文标题】:Put C++ class in C-struct for for use in C++ function (C/C++ mixed code) 【发布时间】:2021-09-20 08:13:46 【问题描述】:注意:这与 C/C++ 链接或 extern 关键字无关。在将我的问题与看似相似的问题联系起来之前,请仔细阅读,谢谢!
我认为这是一个有点不寻常的问题,因为我在浏览网页时没有找到任何相关内容。
我正在编写一个嵌入式系统。主模块是用 C 编写的,而子模块是用 C++ 编写的。为了说明这一点:
submodule.hpp
/ \
/ \
main.c submodule.cpp
现在我想保留 C++ 子模块使用的数据,并将其包含在我的主脚本中的静态变量中,这样我就可以从 main 调用子模块函数并为每次调用提供上下文数据。但是该数据还必须包含一个类,因为它被子模块函数使用,但当然 C 不知道如何处理类。所以我必须在不注意 main.c-script 的情况下偷偷地将一个类放入我的 C 结构中(给我一个错误)。我怎样才能做到这一点?
我认为这样的事情可以工作:
struct data_for_cpp_submodule
int a;
int b;
void *class;
然后将 void 指针“类”转换回适当的类,以便我可以在 C++ 脚本中使用它。这可以工作还是我完全错误的方式?
【问题讨论】:
我会考虑以相反的方式进行。使用 C-struct 并实现一个可以处理它的类。反过来,似乎介于不可能和不明智之间。 携带指向 C++ 类的指针很好。在 C 代码中取消引用该指针将产生未定义的行为(在特定情况下除外,例如类是 POD)。只要您转换为有效类型(例如,如果对象的类型为A
,则将指针转换为 A*
),在 C++ 代码中进行转换即可。如果您将 struct data_for_cpp_submodule
从 C 传递到 C++ 代码,则该类型的定义需要在 C 和 C++ 中都有效 - 这意味着有几件事,例如指针不能命名为 class
(因为 class
是C++ 中的关键字)。
您可以这样做,但要考虑所有权。如果 C 代码拥有该对象,则每次初始化和取消初始化都需要 1 个函数(基本上是对 C++ 对象使用 new
/delete
的功能)。如果对象归 C++ 方所有,则任何适合标识该对象的唯一 ID 就足够了。你可以例如在向量中创建对象并为您的 C 代码提供索引;只要您可以保证在 C++ 端删除对象后,C 代码不会尝试访问该对象,提供一个 void 指针就可以了。
我认为这样的事情可能会起作用:这只是因为 C++“类”实际上只不过是一个 C struct
。可以包含在 C struct
中的唯一 类型的类只不过是 C struct
。其他任何事情都无法完成,因为 C 没有语法来处理实际的 C++ class
构造。因此,将 C struct
硬塞到 C++ 类中的唯一事情就是将来有人将实际的 C++ 特定代码放入 C-struct-masquerading-as-a-C++-class 并破坏事物。希望非常壮观,因此可以立即修复...
一个问题:你不能命名那个指针class
。 class
是 C++ 中的保留字。
【参考方案1】:
请注意,OP强调:
主模块是用 C 编写的,而子模块是用 C++ 编写的。
恕我直言,为 C++ submodule
添加 C 绑定完全没问题,这样它就可以在 C 代码中使用。
OP 的例子(增强一点使其成为MCVE):
submodule
的 C++ 代码:
标头submodule.hpp
:
#ifndef SUB_MODULE_HPP
#define SUB_MODULE_HPP
class Test
private:
int a = 0;
int b = 0;
public:
Test() = default; // default constructor
Test(int a, int b): a(a), b(b) // value constructor
Test(Test&) = delete; // say: copy constructor disabled
Test& operator=(Test&) = delete; // say: copy assignment disabled
public:
int exec() const;
;
#endif // SUB_MODULE_HPP
来源submodule.cpp
:
#include "submodule.hpp"
int Test::exec() const return a + b + 42;
submodule
的 C 绑定:
标头:submodule.h
#ifndef SUB_MODULE_H
#define SUB_MODULE_H
#ifdef __cplusplus
extern "C"
#endif
struct TestC_;
typedef struct TestC_ TestC; // opaque structure
extern TestC* testNew();
extern TestC* testNewValues(int a, int b);
extern void testDelete(TestC *pTest);
extern int testExec(TestC *pTest);
#ifdef __cplusplus
#endif
#endif // SUB_MODULE_H
来源submoduleC.cpp
:
#include "submodule.h"
#include "submodule.hpp"
TestC* testNew() return (TestC*)new Test();
TestC* testNewValues(int a, int b) return (TestC*)new Test(a, b);
void testDelete(TestC *pTest) delete (Test*)pTest;
int testExec(TestC *pTest) return ((Test*)pTest)->exec();
最后但同样重要的是:使用 submodule
的示例 C 程序:
#include <stdio.h>
#include "submodule.h"
int main(void)
TestC *pTest = testNew();
printf("pTest->exec(): %d\n", testExec(pTest));
testDelete(pTest);
pTest = testNewValues(123, -123);
printf("pTest->exec(): %d\n", testExec(pTest));
testDelete(pTest);
如何编译和链接g++
/gcc
:
$ ## compile C++ code
$ g++ -std=c++17 -O2 -Wall -pedantic -c submodule.cpp submoduleC.cpp
$ ## compile C code
$ gcc -std=c11 -O2 -Wall -pedantic -lstdc++ main.c submodule.o submoduleC.o
$ ## test
$ ./a.out
输出:
testExec(pTest): 42
testExec(pTest): 42
Demo on coliru
最值得注意的部分可能是opaque structure
:
struct TestC_;
typedef struct TestC_ TestC; // opaque structure
(这是我在开始编写自己的绑定之前从其他 C 绑定中学到的。)
opaque structure
实际上是一个不完整的结构,但这是有意为之。
不透明结构旨在仅在 C 中用作指针。它不旨在允许取消引用任何内容(并且,对于不完整的结构,编译器只会拒绝这样做)。它的唯一目的是通过 C 代码“隧道”指向 C++ Test
实例的指针。
在Test
(submoduleC.cpp
) 的 C 绑定中,TestC*
在必要时简单地转换为 Test*
(反之亦然)。
提示:
我必须链接-lstdc++
(标准 C++ 库)。否则,我会收到有关运算符 new
和 delete
的未定义引用的投诉:
submoduleC.o: In function `testNew':
submoduleC.cpp:(.text+0xa): undefined reference to `operator new(unsigned long)'
submoduleC.o: In function `testNewValues':
submoduleC.cpp:(.text+0x30): undefined reference to `operator new(unsigned long)'
submoduleC.o: In function `testDelete':
submoduleC.cpp:(.text+0x4b): undefined reference to `operator delete(void*, unsigned long)'
collect2: error: ld returned 1 exit status
其他想法(但可能不那么相关):
我记得有一个著名的框架
用C编写 并提供 C++ API(也称为 C++ 绑定)。我说的是GTK+ 和gtkmm(它的C++ 绑定)。 因此,应该提到 GTK+ 实际上是一个 OOP 库——提供用 C 实现的面向对象的类。(我不确定这个事实是否会使 C++ 绑定的事情变得更容易或更难。)
两者都是开源项目。因此,每个人都可以看看他们是如何做到的。曾经看过,印象深刻……
除此之外,我总是建议相反:用 C++ 编写一个库并添加一个 C 绑定,因为这对我来说似乎更容易。
出于多种原因,我们总是在 Windows 中这样做:
实现不同版本Visual Studio编译的DLL可以链接在一起。 (不久之前,这在 C++ 中几乎是不可能的,但现在,我相信,我读到了 Microsoft 引入的 ABI,它可能使之成为可能。)
为其他语言的绑定提供基础,例如C#。
【讨论】:
这并没有真正回答如何将实际的 C++ 类放入 Cstruct
的问题。这个问题当然不能回答,因为做不到……以上是关于将 C++ 类放入 C-struct 以供 C++ 函数使用(C/C++ 混合代码)的主要内容,如果未能解决你的问题,请参考以下文章
将 c-struct 放入 NSArray 的最佳方法是啥?
如何将类中的函数放入线程中? (使用 Boost 的 C++)