Подтвердить что ты не робот

С++ две библиотеки зависят от одной и той же библиотеки, но разных версий?

Если у меня есть libs A, B и C в С++ с использованием компилятора GCC. Lib A и B зависят от C, но от разных версий. Могу ли я использовать A и B вместе в другой программе? или будут ли разные версии, требуемые от C конфликтами A и B? И как я могу решить эту проблему и могу ли я?

4b9b3361

Ответ 1

Я предполагаю, что вы связываете динамически. Если оба A и B полностью инкапсулируют их соответствующие версии C, возможно, это будет возможно. Возможно, вам придется убедиться, что разные версии C называются по-разному (то есть libMyC.1.so и libMyC.2.so), чтобы избежать путаницы, когда они загружаются во время выполнения.

Вы также можете исследовать статическое построение A и B, чтобы избежать возможности беспорядка загрузки во время выполнения.

Самый простой способ узнать это просто попробовать. Это не должно занять много времени, чтобы определить, будет ли это работать или нет.

Наконец, безусловно, самое легкое решение и лучше всего с точки зрения обслуживания - довести A или B до уровня другого, чтобы они использовали одну и ту же версию C. Это лучше в так много способов, и я настоятельно призываю сделать это, а не пытаться решить реальную проблему.

Ответ 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.

Ответ 3

Я нашел этот вопрос в поиске ответов и, как предложил @Component-10, создал минимальный набор файлов для исследования этого поведения и протестировал его с помощью MacOS + CLANG.

  • Если вы создаете A и B как общие библиотеки, вы можете получить соответствующее разрешение зависимой библиотеки C, к которой относятся зависимости A и B, но в разных версиях.
  • Если здание A и B статично, оно терпит неудачу.

ИЗМЕНИТЬ

Как указано в комментариях, подход с разделяемой библиотекой не является кроссплатформенным и не работает в Linux.

@SergA создала решение с API-интерфейсом Dynamically Loaded Library (dl) (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;
}

Предыдущее решение, показывающее static vs. shared

Вот мой набор файлов. Я не буду показывать их здесь для краткости.

$ 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, который вызывает libC v1 call_C, а libB содержит call_B, который вызывает libC v1 call_C.

Затем main_A ссылается только на libA, main_B только на lib_B и main_AB на оба.

compile_static_fails.sh

Следующий набор команд строит 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, он идет не туда!

compile_shared_works.sh

#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

Выходной сигнал

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 [v2]

Он работает (на MacOS)!