码迷,mamicode.com
首页 > 其他好文 > 详细

编写二进制兼容的库

时间:2014-09-05 23:35:12      阅读:367      评论:0      收藏:0      [点我收藏+]

标签:http   io   使用   ar   for   文件   div   问题   cti   

    开发小组公共库的过程中,遇到二进制兼容问题。下面是二进制兼容和代码兼容的具体定义:

A library is binary compatible, if a program linked dynamically to a former version of the library continues running with newer versions of the library without the need to recompile.

If a program needs to be recompiled to run with a new version of library but doesn‘t require any further modifications, the library is source compatible.

  举个简单的例子,一个随机数生成库提供2个接口:设置种子set_seed和生成随机数my_rand。

  liba.h 

#ifndef _LIBA_H
#define _LIBA_H

struct A
{
	int seed;
};

int set_seed(struct A* a, int s);

int my_rand(struct A a, int b);

#endif

 liba.c

#include "liba.h"

int  set_seed(struct A* a, int s)
{
	a->seed = s;
	return 0;
}

int my_rand(struct A a, int b)
{
	int r = a.seed + b;
	return r;
}

编译成动态库libcutil.so,调用方代码也很简单:

main.c

#include "liba.h"
#include <stdio.h>
#include <dlfcn.h>

int main()
{
	void* handle=dlopen("libcutil.so", RTLD_LAZY);
	if(!handle)
	{
		fprintf(stderr, "%s\n", dlerror());
		return -1;
	}
	struct A a;
	int (*fa)(struct A*, int);
	fa = dlsym(handle, "set_seed");
	fa(&a, 31);
	
	int (*fb)(struct A, int);
	fb = dlsym(handle, "my_rand");
	int b = fb(a, 4);
	printf("%d\n", b);
	return 0;
}

这样能正常工作:

export LD_LIBRARY_PATH="../api_r1/:$LD_LIBRARY_PATH"; ./main 
35

第二版本,在liba.h里面的struct A增加了一个成员:

#ifndef _LIBA_H
#define _LIBA_H

struct A
{
	int add; //增加了一个成员
	int seed;
};

int set_seed(struct A* a, int s);

int my_rand(struct A a, int b);

#endif

 liba.c几乎不变:

#include "liba.h"

int  set_seed(struct A* a, int s)
{
	a->add = 123; //增加此行
	a->seed = s;
	return 0;
}

int my_rand(struct A a, int b)
{
	int r = a.seed + b;
	return r;
}

 编译后,生成新的动态库libcutil.so,使用方不重新编译代码,运行:

export LD_LIBRARY_PATH="../api_r2/:$LD_LIBRARY_PATH"; ./main 
4

结果错误,这个库libcutil.so二进制不兼容。

原因: 库的头文件liba.h中不但包含了接口,还包含了实现相关的代码(struct A)。使用此库的代码(main.c)include了此头文件,自然就包含了struct A的定义,而libcutil.so完全依赖struct A的定义。不编译客户代码,只替换库时,库中依赖的struct A(最新版)与使用库的代码中struct A(老版)不一致。

解决方案:

原理--头文件中只暴露接口,实现相关的代码全部放在.c/.cpp中,实现与接口分离。

实现--通过一个指针增加一层indirection解耦。

具体有2个方案:

1. C接口使用void*指针指向具体的struct。

#ifndef _LIBA_H
#define _LIBA_H

typedef void* Api;

int init(Api *api, int s); //初始化
int run(Api api, int b);
int release(Api api);      //释放资源

#endif

 .c文件包含所有跟实现相关的代码:

#include "liba.h"
#include <stdio.h>
#include <stdlib.h>

struct A
{
	int seed;
};

typedef struct A* ApiII;

int init(Api* api, int s)
{
	ApiII p = (ApiII)malloc(sizeof(struct A));
	if(api == NULL || p == NULL)
		return -1;
	p->seed = s;
	*((ApiII*)api) = p;
	return 0;
}

int run(Api api, int b)
{
	if(api == NULL)
		return -1;
	((ApiII)api)->seed+=b;
	return ((ApiII)api)->seed;
}

int release(Api api)
{
	if(api != NULL)
	{
		free(api);
		api = NULL;
	}
	return 0;
}

 2. C++接口使用pimpl

#ifndef _LIBA_H
#define _LIBA_H

class A
{
public:
	A();
	~A();
	int init(int s);
	int run(int b);
private:
	class Aimpl;   //前置申明,在类A中
	Aimpl* pimpl;  //暴露一个指针,多一层引用
};
#endif

 .cpp代码:

#include "liba.h"
#include <stdio.h>

class A::Aimpl
{
public:
	Aimpl(int s):seed(s){}
	int run(int b)
	{
		seed+=b;
		return seed;
	}
private:
	int seed;
};

A::A():pimpl(NULL)
{
}

A::~A()
{
	if(pimpl != NULL)
	{
		delete pimpl;
		pimpl=NULL;
	}
}

int A::init(int s)
{
	if(pimpl != NULL)
		delete pimpl;
	pimpl = new A::Aimpl(s);
	return 0;
}

int A::run(int b)
{
	if(pimpl == NULL)
		return -1;
	return pimpl->run(b);
}

 

附件包含相关代码。

参考文献:

https://techbase.kde.org/Policies/Binary_Compatibility_Issues_With_C++#Definition

 

编写二进制兼容的库

标签:http   io   使用   ar   for   文件   div   问题   cti   

原文地址:http://www.cnblogs.com/Torstan/p/3958818.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!