V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
shizukupr
V2EX  ›  C++

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

  •  
  •   shizukupr · 2023-07-21 20:17:56 +08:00 · 1906 次点击
    这是一个创建于 468 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目前手上有一个将用 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 *的效果。

    第 1 条附言  ·  2023-07-21 21:40:08 +08:00
    上面的实现中不同类所对应的 kernel 内部实现完全不同
    12 条回复    2023-09-18 22:31:43 +08:00
    exch4nge
        1
    exch4nge  
       2023-07-21 20:27:20 +08:00 via iPhone
    一般解法:继承+虚函数,不知道你这个场景能否承担相应开销
    pocarisweat
        2
    pocarisweat  
       2023-07-21 20:37:14 +08:00
    如果是这些结构体数据成员不同,但要实现相似(但不相同)的行为,可以用继承搭配虚函数。

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

    如果这些数据逻辑上不需要统一管理,放在一起只是为了复用代码,那可以考虑引入模板,然后不同类型各管各的,利用模板复用同一套代码。
    codehz
        3
    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
        4
    leonshaw  
       2023-07-21 20:42:22 +08:00 via Android
    有共性就继承,没有就 std::any ,直接用 void* 也没啥不行的。
    favourstreet
        5
    favourstreet  
       2023-07-21 21:40:10 +08:00 via Android
    虽然有些离题,不过我想说 C 改 C++我不明白意义何在,直接用 c++写一层接口去调用原来的程序,把 c 的部分包起来不行吗
    shizukupr
        6
    shizukupr  
    OP
       2023-07-22 00:14:22 +08:00
    @favourstreet 确实这边有一些私有的东西得依赖一些 C++里面才有的东西,涉及到重写算法逻辑,所以被迫要求完全重构
    ysc3839
        7
    ysc3839  
       2023-07-22 03:11:53 +08:00 via Android
    C++没有不能使用 void*一说吧?你这种情况感觉是在自己实现 std::variant ,但又不完全像,或者说是一种自己实现的 RTTI 。std::variant 的基本原理就是用一个变量存储当前类型,然后各种类型都用 union 合在一起。
    建议给更多细节,便于判断。
    philon
        8
    philon  
       2023-07-22 17:25:52 +08:00
    可能是我没理解你的需求,如果只是单纯想要在 C++中向 C 一样传递任意类型,既然你都定义为指针了,那就只认地址,传参的时候强转为 void*就行了;如果你要利用 C++的特性,那 std::bind+std::function 可能更适合
    iceheart
        9
    iceheart  
       2023-08-07 07:12:23 +08:00 via Android
    抽象类不就是干这个的么
    struct Interface {
    virtual Kernal() = 0;
    };
    xgdgsc
        10
    xgdgsc  
       2023-08-09 21:00:23 +08:00 via Android
    如果不是需要在资源受限环境跑的话,科学计算的程序还是 julia 写编译成 c 库调用最优雅
    weeei
        11
    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
        12
    weeei  
       2023-09-18 22:31:43 +08:00
    @weeei sizeof 是操作符,编译期间就确定结果了。应该使用 typeid(*p).name() 判断名称,才能在运行期间确定子类型。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1239 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 23:15 · PVG 07:15 · LAX 16:15 · JFK 19:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.