求教如何在 C++中优雅地实现在 C 中的 void *所能实现的部分功能

2023-07-21 20:17:56 +08:00
 shizukupr

目前手上有一个将用 C 实现的科学计算程序重写为 C++实现的任务,需要将其中大量的 C 风格实现改为 OOP 风格实现,但是目前遇到了一些实现上的问题

原始程序中使用struct维护一个全局计算数据结构,所有的计算 kernel 又会单独使用一系列的struct来维护每个 kernel 所需要的参数。这些 kernel 参数结构体在用于保存全局计算的结构体中使用了void *进行管理,然而在迁移到 C++的过程中要求不能使用void *,故问一问各位大佬如何在 C++中优雅且低成本地实现类似void *的功能

在原始的程序中,全局计算结构体存在这样的成员

typedef struct {
 // ...

void *something[N];
 
 // ...
} GlobalStruct;

其中这个void *可能对应多个不同的结构体,各个结构体之间相互有不同的成员,例如

typedef struct {
	int a;
    int b;
    short c;
} A;

typedef struct {
	int a;
    int b;
    double d;
} B;

typedef struct {
	int a;
    int b;
} C;

void kernel_A(A *data);
void kernel_B(B *data);
void kernel_C(C *data);

请问有什么方法可以在 C++程序中实现在上面void *的效果。

1962 次点击
所在节点    C++
12 条回复
exch4nge
2023-07-21 20:27:20 +08:00
一般解法:继承+虚函数,不知道你这个场景能否承担相应开销
pocarisweat
2023-07-21 20:37:14 +08:00
如果是这些结构体数据成员不同,但要实现相似(但不相同)的行为,可以用继承搭配虚函数。

如果是单纯想把不一样的数据存在一起,可以用 std::variant (C++17).

如果这些数据逻辑上不需要统一管理,放在一起只是为了复用代码,那可以考虑引入模板,然后不同类型各管各的,利用模板复用同一套代码。
codehz
2023-07-21 20:39:55 +08:00
https://en.cppreference.com/w/cpp/utility/variant
把所有可能的类型都写上去
然后处理函数可以做成重载,或者用 https://en.cppreference.com/w/cpp/utility/variant/visit 里提示的 overloaded 方法
leonshaw
2023-07-21 20:42:22 +08:00
有共性就继承,没有就 std::any ,直接用 void* 也没啥不行的。
favourstreet
2023-07-21 21:40:10 +08:00
虽然有些离题,不过我想说 C 改 C++我不明白意义何在,直接用 c++写一层接口去调用原来的程序,把 c 的部分包起来不行吗
shizukupr
2023-07-22 00:14:22 +08:00
@favourstreet 确实这边有一些私有的东西得依赖一些 C++里面才有的东西,涉及到重写算法逻辑,所以被迫要求完全重构
ysc3839
2023-07-22 03:11:53 +08:00
C++没有不能使用 void*一说吧?你这种情况感觉是在自己实现 std::variant ,但又不完全像,或者说是一种自己实现的 RTTI 。std::variant 的基本原理就是用一个变量存储当前类型,然后各种类型都用 union 合在一起。
建议给更多细节,便于判断。
philon
2023-07-22 17:25:52 +08:00
可能是我没理解你的需求,如果只是单纯想要在 C++中向 C 一样传递任意类型,既然你都定义为指针了,那就只认地址,传参的时候强转为 void*就行了;如果你要利用 C++的特性,那 std::bind+std::function 可能更适合
iceheart
2023-08-07 07:12:23 +08:00
抽象类不就是干这个的么
struct Interface {
virtual Kernal() = 0;
};
xgdgsc
2023-08-09 21:00:23 +08:00
如果不是需要在资源受限环境跑的话,科学计算的程序还是 julia 写编译成 c 库调用最优雅
weeei
2023-09-18 19:39:48 +08:00
OP 的意思应该是:void *something[N]; 这个数组里面存的不同类型的数据,所以原来的写法用了 void * 表示,现在是想设计的优雅一些。这是设计模式的问题了。
解决方法:基本的所有的数据类型都继承一个基类 struct Base {}; C++ 是允许空结构体的,如果子类型没有任何共同点,基类 Base 就定义为空结构体,void *something[N]; 就可以变成 Base *something[N]; 拿到后再根据 sizeof() 确定子类型,或者严谨一点可以给 Base 定义一个 type 字段。
weeei
2023-09-18 22:31:43 +08:00
@weeei sizeof 是操作符,编译期间就确定结果了。应该使用 typeid(*p).name() 判断名称,才能在运行期间确定子类型。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/958698

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX