6. 对外接口设计
大家好,我是程序喵。
在前面章节我介绍过项目的结构设计,这篇文章我主要向大家介绍如何设计对外接口。
因为我们做的是SDK,SDK的产物一般是库二进制文件 + 头文件,头文件主要包含对外暴露的接口,用户使用SDK的功能其实也就是调用这些对外暴露的头文件中的接口(不要较真,我们这里不考虑骚操作)。
一个合格的SDK,一定要让用户接入方便,调用的接口越少,接入方式越简单越好,减少各个函数排列组合的个数,防止用户调用函数顺序错误导致SDK功能异常。
这里,SDK会对外导出C接口,而不是暴露C++接口,为什么?
C接口的语言交互性是最高的,用户不仅可以用C语言调用,也可以用C++调用,也可以用Go、Python、Rust等其它语言调用。
C接口ABI兼容性最高,用llvm编译器导出的C++符号,可能使用MSVC或者gcc就无法调用,但是C接口就永远不会这样。
我这里先贴出对外暴露的C接口
这里对外只暴露4个接口,和一个struct。
因为我们需要支持多实例,所以设计出 Buried_Create 和 Buried_Destroy 接口。
Buried_Create 用于创建一个实例,work_dir作为这个实例的工作目录,一个实例一定会在对应的工作目录下生成文件等,不会污染其它目录。
Buried_Destroy 用于销毁一个实例,有创建就有销毁,这里应该不用过多介绍。
Buried_Start 用于开启埋点上报能力,第一个参数buried,传入具体的实例;表示在哪个实例下工作,第二个参数config,表示这个实例对应的配置,其中:
- host:要上报的url
- port:对应的端口号
- topic:对应的topic,一般会上传到url:port/topic下
- user_id:用户的ID,方便分析信息,比如哪个用户使用app出了异常等
- app_version:应用的版本号
- app_name:应用的名称
- custom_data:自定义数据,Json的字符串,因为各个用户肯定想携带不同的数据,使用这个custom_data正好。
Buried_Report 用于上报具体的数据,第一个参数buried,传入具体的实例;第二个参数title,这条数据的标题,这个标题可以用于展示在网站端;第三个参数data,表示详细数据;第四个priority,表示优先级,内部模块上报时会按照优先级来上报数据,优先级高的数据会优先上报。
虽然对外暴露的是C接口,但是对内可以使用C++开发。
因为对外Header中只有一个 typedef struct Buried Buried; 语句,我们还不知道这个Buried的具体定义。
它在内部其实有的是C++的定义,定义如下:
然后,我们做一个桥接层就可以巧妙的做这种转换:
代码其实也比较简单,Buried_Create对应new操作,Buried_Destroy对应delete操作。
Buried_Start 和 Buried_Report 中的第一个参数buried,在内部会自动转成C++的指针,进而调用相关的函数。
介绍完对外的接口设计与C和C++的桥接层后,有一点我们还需要了解。
因为我们对外给出的是个DLL动态库,动态库需要导出符号才可以给外部调用,否则外部无法使用这些函数。
如何导出符号?
直接使用 #define BURIED_EXPORT __declspec(dllexport) 就OK。
然后可以使用dumpbin /exports 方法查看是否导出符号成功

Win导出符号有两种方法,详见:https://learn.microsoft.com/zh-cn/cpp/build/determining-which-exporting-method-to-use?view=msvc-170
到这里,本节内容已经介绍完毕,查看本节详细代码可以移步:https://github.com/chengxumiaodaren/BuriedPoint/tree/0.1.4.1
