开发共享库需要遵循的原则

来源:互联网 发布:photo shop mac破解版 编辑:程序博客网 时间:2024/04/30 15:12

共享库是给其他程序员使用的,库设计的好不好,不只是自己的问题,还会影响其他程序。

根据经验,我总结了几条设计库时需要遵守的规律

 

1 库的通用接口类型

大致分为两种类型

1)库提供的接口,需要在调用之间保持某些信息。

需要提供类似Open/Close的接口,如LIBXX_Handle libxx_Open(struct xx);libxx_Close(LIBXX_Handle)

LIBXX_Handle是一个数据结构的指针,Open内部分配并返回该指针,库的所有内部状态都在该数据结构中。

2)库提供的接口,不需要在调用之间保持信息

      上述Open/Close就是不需要的

 

另外,暴露出来的应该是接口(函数),尽可能不要暴露数据(全局变量)

 

2 信息隐藏

凡是属于库内部实现相关的东西,都不应该暴露给用户。因为一方面会使用户使用该接口麻烦,容易出错,另一方面限制了库的扩展性,一旦要修改库的内部实现,那么用户的程序也必须修改。

 

3 名字空间问题

1 最好携带库的名字作为前缀(例如ffmpeg中的libavcodec提供的接口函数,都以avcodec_开头),以减少名字空间冲突。试想,某个应用程序用到了两个第三方的库,这两个库提供了同名函数ParseData,是不是会很头大?

2 符号名不要怕长,最好能通过名字,大致猜出用途

 

4 线程安全问题

除非有足够强大的理由,否则设计库的时候,一定要注意线程安全,因为用户很可能在多线程中调用该库

1 不得写入全局变量。如果有这种需要,说明接口类型应该为类型1,可以放到LIBXX_Handle

2 不得使用局部static变量。如过有这种需要,说明接口类型应该为类型1,可以放到LIBXX_Handle

3 其他违反多线程原则的情况,如linux下就不应该使用signal系列函数,另外一些C库函数应该使用线程安全版本

 

 

5 版本与编译环境问题

如果库的接口参数,有struct类型,则必须注意版本与编译环境问题。一般来说,struct类型的第一个成员应该是一个int iSize,每次调用把该成员初始化成struct的长度。

例如struct xx{ int iSize; int iData1;char cData2}; struct xx instance1; instance1.iSize = sizeof(instance1);

为什么需要这个神奇的成员呢?iSize成员的作用如下:

1 版本一致性检查。如果应用程序编译时使用的动态库版本,和系统中实际安装的库版本不一致,那么对同一结构,应用程序传送给库的iSize的大小很可能和库希望的大小不一致,

这种情况下,库应返回一个错误码

2 编译环境一致性检查。如果库编译时的struct对齐设置和应用程序编译时的struct对齐设置不一样,那么应用程序传送给库的iSize的大小和库希望的大小不一致,库就可以发现这种情况并返回错误码。

如果没有iSize字段,这种情况下程序必然死掉

 

6 内存管理一致性

应用程序分配的内存,由应用程序释放;库分配的内存,由库负责释放。有些情况下,库需要提供一个接口,释放其分配的内存。

应用程序不要直接用free/delete 等释放库分配的内存,因为库可能有自己内部的内存管理机制

原创粉丝点击