我看不出你怎么能期望任何库都兼容。如果可能的话,就不会有这么多编译的库变体。
例如,只要正确设置调用,就可以从 16 位程序调用 64 位库。但是您必须知道您正在调用基于 64 位的库。
可移植性是一个备受关注的目标,但很少有人真正实现它。经过 30 多年的系统级、固件和应用程序编程,我认为这更像是一个幻想而不是一个目标。不幸的是,硬件迫使我们针对硬件进行优化。因此,当我编写库时,我使用以下内容:
- 为 ABI 编译
使用指向结构的指针用于所有函数调用的输入和输出:
int lib_func(struct *input, struct *output);
返回的 int 仅指示错误。我使所有错误代码都是唯一的。我要求用户在使用该库之前调用一个 init 函数。用户称其为:
lib_init(sizeof(int), sizeof(char *), sizeof(long), sizeof(long long));
这样我就可以决定是否会有任何麻烦或在需要时修改任何假设。除了版本号之外,我还添加了一个功能,允许用户了解我的数据大小和对齐方式。
这并不是说用户或我被期望“即时”修改代码或花费大量 CPU 功率重新设计结构。但这允许应用程序绝对确保它与我兼容,反之亦然。
我过去采用的另一个选项是在我的库中简单地包含几个入口点函数。例如:
int lib_func32();
int lib_func16();
int lib_func64();
它给你带来了一些麻烦,但你可以使用预处理器来修复它:
#ifdef LIB_USE32
#define lib_function lib_func32
#endif
您可以对数据结构执行相同的操作,但我建议无论 CPU 大小如何都使用相同大小的数据结构——除非性能是重中之重。再次,回到硬件!
我探索的最后一个选项是是否具有所有大小和样式的输入函数,将输入转换为我的库的期望,以及我的库的输出。
例如,您lib_func32(&input, &output)
可以编译为期望 32 位对齐的 32 位指针,但它将 32 位结构转换为您的内部 64 位结构,然后调用您的 64 位函数。当它返回时,它将 64 位结构重新格式化为调用者所指向的 32 位等效结构。
int lib_func32(struct *input32, struct *output32)
{
struct input64;
struct output64;
int retval;
lib_convert32_to_64(input32, &input64);
retval = lib_func64(&input64, &output64);
lib_convert64_to_32(&output64, output32);
return(retval);
}
总之,完全便携的解决方案是不可行的。即使您从完全可移植性开始,最终您也将不得不偏离。这是事情真正变得混乱的时候。您会因偏差而破坏您的风格,从而破坏您的文档并使用户感到困惑。我认为最好从一开始就计划好。
硬件总是会导致你有偏差。只需考虑“字节顺序”造成的麻烦——更不用说每天交换字节顺序所使用的 CPU 周期数。