編寫二進位相容的庫

來源:互聯網
上載者:User

標籤: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_Hstruct 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_Hstruct 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_Htypedef 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_Hclass 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

 

編寫二進位相容的庫

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.