1
wzzzx 2021-12-01 10:32:13 +08:00
这里可以借鉴一下 C++的 Pimpl 思想
|
2
ysc3839 2021-12-01 10:37:02 +08:00
把函数指针都放在一个 struct 里面,struct 头部存储当前版本的 sizeof ,新增函数都放到最后,结构体变了就新增一个函数
|
3
newmlp 2021-12-01 10:41:50 +08:00
Pimpl +1 类似于 Qt 的 d 指针,类的私有成员包装成另一个类,然后通过指针去访问,这样对外的类就只包含 public 的成员和一个私有的指针,更改结构体就不会影响对外类的内存布局了
|
4
FranzKafka95 OP 我可能没太说清楚,举个例子吧。
如在头文件版本 1 中,有这样一个结构体(这个结构体我们自己与第三方都有使用): struct A{ int a; float b; … } 在头文件版本 2 中,需要在结构体 A 中增加成员 c ,增加取下: struct A{ int a; float b; double c; … } 第三方程序最终会编译成一个动态库(so)。现在的构想是头文件变动(如结构体成员增加),但是第三方 so 不变,怎样实现头文件向下兼容呢 |
5
FranzKafka95 OP @newmlp pimpl 好像不太使用,这里的接口都是第三方实现的,由我们调用,而且是我们制定了接口,包括接口传参,现在就是传参是一个结构体,这个结构体包含很多第三方需要的信息,我要改变这个结构体(追加成员),而第三方程序不做改变,实现向下兼容
|
6
FranzKafka95 OP 其实我很好奇,程序中索引结构体成员不是根据符号索引的(没有符号?)貌似是根据类型直接取内存块里的数据,暂且不论关闭结构体对齐与否都会存在问题(因为还有结构体嵌套),感觉无解了😭
|
7
xylxAdai 2021-12-01 11:15:37 +08:00
@FranzKafka95 。。。不太理解,第三方如果用不到这个新加的结构体变量的话,那么你直接内部用自己的结构体,用到它们接口再转过去就好了嘛。。
|
8
3dwelcome 2021-12-01 11:21:23 +08:00
传什么 struct 哦,太落后了。
学 JS 理念,交互接口就只传一个 json 对象,以后怎么变都没问题。 |
9
FranzKafka95 OP @xylxAdai 是这样的,这一份头文件会对接多个第三方,新增加的成员对新的第三方库有用,但是对已经量产的第三方库不会使用。我想保持一份头文件。
|
10
FranzKafka95 OP @3dwelcome 得分具体应用场景,我的应用与第三方库是存在大量数据交互的,传 Json 对象一点不实用
|
11
Sephirothictree 2021-12-01 11:40:07 +08:00
需要使用新结构体的地方,可以单独定义一下扩展参数,例如,旧头文件 struct A;
新第三方库,内部添加一个 struct B { struct A; double c; }; 这样对于新第三方库可以传参后强制转换 ,test(A*t){ struct B * b = (struct B *)t; .... } |
12
3dwelcome 2021-12-01 11:42:56 +08:00
json 实用啊,意味着你头文件只需要提供一个获取函数就可以,不用提供结构变量给第三方访问。
比如就一个函数 void get_keyvalue(char* inname, char* outarray, int* outarraycount); 对方获取内容时,就一句 get_keyvalue, 不同版本可以把 inname 的属性无限扩展,就和 json 一毛一样。 |
13
Sephirothictree 2021-12-01 11:43:15 +08:00
前提就是新第三方 so ,要按照 struct B 来编写代码
|
14
weidaizi 2021-12-01 11:43:49 +08:00
楼主是给用户头文件和 so ,来连接你们的服务的意思吗? 如果是的话,兼容主要做在服务里就可以了,比如最简单的方法是在传输协议的头中带上版本号,注册回调函数的时候,应该是消息+版本号确定唯一的回调函数,比如你上面自己举的例子:
------------------------------------ 版本 1.0.0: struct A{ int a; float b; … }; 那么传输的包中,应该类似于这样 | v1.0.0 | msg_type | payload | ------------------------------------ 版本 1.0.1: struct A{ int a; float b; double c; … } 那么传输的包中,应该类似于这样 | v1.0.1 | msg_type | payload | ------------------------------------ 但是问题又来了,你现在已经发出去了 so ,里面没有版本号怎么办? 其实也简单,可以设置一些特殊的魔法字来识别,比如旧的版本,你的自定义头是这样 | msg_type(int32) | payload | 然后在新版本中,你的自定义头应该是这样 | 特定的 msg_type(int32) | version | msg_type(int32) | payload | 当你读到 特定的 msg_type 时,你知道这是带上来 version 版本的传输协议,接着转而解析 version + msg_type 即可 |
15
learningman 2021-12-01 11:48:36 +08:00
#IFDEF VERSION_XXX
.... #ENDIF |
16
icylogic 2021-12-01 16:41:51 +08:00 via iPhone
> 第三方程序最终会编译成一个动态库(so)。现在的构想是头文件变动(如结构体成员增加),但是第三方 so 不变,怎样实现头文件向下兼容呢
source/binary compatibility 的问题看 kde 这篇就好 https://community.kde.org/Policies/Binary_Compatibility_Issues_With_C%2B%2B#The_Do.27s_and_Don.27ts 还有一个例子是 v4l2 的 api 在三年前添加了一个 data member https://github.com/torvalds/linux/commit/f35f5d7 |
17
GeruzoniAnsasu 2021-12-11 16:55:44 +08:00
#ifndef __PRODUCT_VER__
typedef struct S_EssentialStruct_v0 S_EssentialStruct #elif __PRODUCT_VER__ > 1 typedef struct S_Essential Struct_01 S_EssentialStruct #endif 你们不会在代码里直接写 struct S_EssentialStruct_v0 吧 |
18
FranzKafka95 OP @GeruzoniAnsasu 我们还真是的,一开始就没有考虑兼容性,后面发现需要跟好几家第三方合作,而第三方的需求都不完全一样,所以才出现了这个问题。另外按照答主的思路,定义一个头文件版本的宏,根据宏去控制结构体成员貌似是个不错的想法。
|
19
GeruzoniAnsasu 2021-12-13 13:09:28 +08:00
@FranzKafka95 原项目里用的结构用的是 typedef 的 type 就很方便偷梁换柱,如果都写 `struct S` 那就…… 来一次全员搜索替换吧
另外动态库层面还有这个 https://sourceware.org/binutils/docs/ld/VERSION.html 用来控制 API 导出版本 |