C ++两个库依赖于相同的lib但不同的版本?

时间:2012-04-27 13:09:24

标签: c++ gcc dependencies

如果我使用GCC编译器在C ++中使用库A,B和C. Lib A和B都依赖于C,但它们的版本不同。我可以在另一个程序中一起使用A和B吗?或者A和B所需的不同版本是否会发生冲突?我如何解决这个问题,可以吗?

4 个答案:

答案 0 :(得分:5)

我假设你是动态链接的。如果A和B都完全封装了它们各自的C版本,则可能会这样做。您可能必须确保以不同的方式命名不同版本的C(即libMyC.1.so和libMyC.2.so),以避免在运行时加载它们时出现混淆。

您还可以静态地构建A和B,以避免运行时负载混淆的可能性。

最简单的方法就是尝试一下。不用花很长时间来确定它是否有效。

最后,当然,到目前为止,最简单的解决方案,从维护的角度来看最好的是将A或B提升到另一个的水平,以便它们都使用相同版本的C.这样做更好这么多方法,我强烈要求你这样做,而不是试图解决一个真正的问题。

答案 1 :(得分:2)

动态库不进行强版本检查,这意味着如果A在C中使用的入口点未发生变化,那么它仍然可以使用C的更高版本。话虽如此, Linux发行版通常使用符号链接文件系统方法来提供版本支持。这意味着如果可执行文件仅设计为与1.2.2一起使用,则可以将其专门链接到查找/usr/lib/mylib-1.2.2

大多数程序都链接在一起以查找一般情况,例如。 /usr/lib/mylib这将与机器上的版本符号链接。例如。 /usr/lib/mylib -> /usr/lib/mylib-1.2.2。如果您没有链接到特定版本并且实际接口没有变化,则向前兼容性不应该成为问题。

如果要检查库A和B是否绑定到特定命名的C版本,可以使用ldd命令检查dll搜索路径。

答案 2 :(得分:2)

我在搜索答案时发现了这个问题,正如@ Component-10建议的那样,我创建了一组最小的文件来调查此行为并使用MacOS + CLANG进行测试。

  • 如果将A和B构建为共享库,则可以获得对依赖库C的正确解析,该依赖库是A和B的依赖关系,但是在不同的版本中。
  • 如果将A和B构建为静态,则会失败。

修改

正如评论中所指出的,共享库方法不是跨平台的,在Linux中不起作用。

@SergA使用动态加载库(dl)API(https://www.dwheeler.com/program-library/Program-Library-HOWTO/x172.html)创建了一个解决方案。

@ SergA使用dlopen

的解决方案
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

// #define DLOPEN_FLAGS RTLD_LAZY | RTLD_LOCAL
#define DLOPEN_FLAGS RTLD_LAZY

#if defined(_WIN32) || defined(__CYGWIN__)
    // Windows (x86 or x64)
    const char* libA = "libA.shared.dll";
    const char* libB = "libB.shared.dll";
#elif defined(__linux__)
    // Linux
    const char* libA = "libA.shared.so";
    const char* libB = "libB.shared.so";
#elif defined(__APPLE__) && defined(__MACH__)
    // Mac OS
    const char* libA = "libA.shared.dylib";
    const char* libB = "libB.shared.dylib";
#elif defined(unix) || defined(__unix__) || defined(__unix)
    // Unix like OS
    const char* libA = "libA.shared.so";
    const char* libB = "libB.shared.so";
#else
    #error Unknown environment!
#endif

int main(int argc, char **argv)
{
  (void)argc;
  (void)argv;

  void *handle_A;
  void *handle_B;
  int (*call_A)(void);
  int (*call_B)(void);
  char *error;

  handle_B = dlopen(libB, DLOPEN_FLAGS);
  if(handle_B == NULL) {
    fprintf(stderr, "%s\n", dlerror());
    exit(EXIT_FAILURE);
  }

  handle_A = dlopen(libA, DLOPEN_FLAGS);
  if(handle_A == NULL) {
    fprintf(stderr, "%s\n", dlerror());
    exit(EXIT_FAILURE);
  }


  call_A = dlsym(handle_A, "call_A");
  error = dlerror();
  if(error != NULL) {
    fprintf(stderr, "%s\n", error);
    exit(EXIT_FAILURE);
  }
  call_B = dlsym(handle_B, "call_B");
  error = dlerror();
  if(error != NULL) {
    fprintf(stderr, "%s\n", error);
    exit(EXIT_FAILURE);
  }

  printf(" main_AB->");
  call_A();
  printf(" main_AB->");
  call_B();

  dlclose(handle_B);
  dlclose(handle_A);

  return 0;
}

以前的解决方案显示静态与共享

这是我的一组文件。为简洁起见,我不会在这里展示它们。

$ tree .
.
├── A
│   ├── A.cc
│   └── A.hh
├── B
│   ├── B.cc
│   └── B.hh
├── C
│   ├── v1
│   │   ├── C.cc
│   │   └── C.hh
│   └── v2
│       ├── C.cc
│       └── C.hh
├── compile_shared_works.sh
├── compile_static_fails.sh
├── main_A.cc
├── main_AB.cc
└── main_B.cc

A取决于C版本1,B取决于C版本2.每个库包含单个函数,例如libA包含调用call_A v1&#39; libC的{​​{1}},call_C包含调用libB v1&#的call_B 39; s libC

然后call_C仅指向main_AlibA仅指向main_Blib_B指向两者。

compile_static_fails.sh

以下命令集静态构建main_ABlibA

libB

输出

#clean slate
rm -f *.o *.so *.a *.exe

#generate static libA
g++ -I . -c C/v1/C.cc A/A.cc
ar rvs libA.a *.o
rm -f *.o

#generate static libB
g++ -I . -c C/v2/C.cc B/B.cc
ar rvs libB.a *.o
rm -f *.o

#generate 3 versions of exe
g++ -L . -lA main_A.cc -o main_A.exe
g++ -L . -lB main_B.cc -o main_B.exe
g++ -L . -lA -lB main_AB.cc -o main_AB.exe
./main_A.exe
./main_B.exe
./main_AB.exe

main_A->call_A->call_C [v1] main_B->call_B->call_C [v2] main_AB->call_A->call_C [v1] main_AB->call_B->call_C [v1] 执行main_AB时,它会转到错误的位置!

compile_shared_works.sh

call_B

输出

#clean slate
rm -f *.o *.so *.a *.exe

#generate shared libA
g++ -I . -c -fPIC C/v1/C.cc A/A.cc
g++ -shared *.o -o libA.so
rm *.o

#generate shared libB
g++ -I . -c -fPIC C/v2/C.cc B/B.cc
g++ -shared *.o -o libB.so
rm *.o

#generate 3 versions of exe
g++ -L . -lA main_A.cc -o main_A.exe
g++ -L . -lB main_B.cc -o main_B.exe
g++ -L . -lA -lB main_AB.cc -o main_AB.exe
./main_A.exe
./main_B.exe
./main_AB.exe

它有效(在MacOS上)!

答案 3 :(得分:1)

@SergA的解决方案在Linux中也可以使用,如果我们使用flag打开共享库 RTLD_LAZY | RTLD_LOCAL
输出为:
1. Main_AB_dlopen-> CallA-> callC(v1)
2. Main_AB_dlopen-> callB-> callC(v2)