如果我使用GCC编译器在C ++中使用库A,B和C. Lib A和B都依赖于C,但它们的版本不同。我可以在另一个程序中一起使用A和B吗?或者A和B所需的不同版本是否会发生冲突?我如何解决这个问题,可以吗?
答案 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进行测试。
修改强>
正如评论中所指出的,共享库方法不是跨平台的,在Linux中不起作用。
@SergA使用动态加载库(dl)API(https://www.dwheeler.com/program-library/Program-Library-HOWTO/x172.html)创建了一个解决方案。
#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_A
,libA
仅指向main_B
,lib_B
指向两者。
以下命令集静态构建main_AB
和libA
。
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
时,它会转到错误的位置!
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)