По крайней мере, в случае статических библиотек вы можете обойти это довольно удобно.
Рассмотрим эти заголовки библиотек foo и bar .Для этого урока я также дам вам исходные файлы
examples / ex01 / foo.h
int spam(void);
double eggs(void);
examples / ex01 / foo.c (это может быть непрозрачно /недоступно)
int the_spams;
double the_eggs;
int spam()
{
return the_spams++;
}
double eggs()
{
return the_eggs--;
}
пример / ex01 / bar.h
int spam(int new_spams);
double eggs(double new_eggs);
examples / ex01 / bar.c (может быть непрозрачным / недоступным)
int the_spams;
double the_eggs;
int spam(int new_spams)
{
int old_spams = the_spams;
the_spams = new_spams;
return old_spams;
}
double eggs(double new_eggs)
{
double old_eggs = the_eggs;
the_eggs = new_eggs;
return old_eggs;
}
Мы хотим использовать их в программе foobar
example / ex01 / foobar.c
#include <stdio.h>
#include "foo.h"
#include "bar.h"
int main()
{
const int new_bar_spam = 3;
const double new_bar_eggs = 5.0f;
printf("foo: spam = %d, eggs = %f\n", spam(), eggs() );
printf("bar: old spam = %d, new spam = %d ; old eggs = %f, new eggs = %f\n",
spam(new_bar_spam), new_bar_spam,
eggs(new_bar_eggs), new_bar_eggs );
return 0;
}
Одна проблема сразу становится очевидной: C не знает перегрузки.Таким образом, у нас есть два раза две функции с одинаковым именем, но с другой подписью.Поэтому нам нужен какой-то способ их различить.В любом случае, давайте посмотрим, что компилятор может сказать по этому поводу:
example/ex01/ $ make
cc -c -o foobar.o foobar.c
In file included from foobar.c:4:
bar.h:1: error: conflicting types for ‘spam’
foo.h:1: note: previous declaration of ‘spam’ was here
bar.h:2: error: conflicting types for ‘eggs’
foo.h:2: note: previous declaration of ‘eggs’ was here
foobar.c: In function ‘main’:
foobar.c:11: error: too few arguments to function ‘spam’
foobar.c:11: error: too few arguments to function ‘eggs’
make: *** [foobar.o] Error 1
Хорошо, это не было неожиданностью, он просто сказал нам, что мы уже знали или, по крайней мере, подозревали.
Так можем ли мы как-то разрешить конфликт идентификаторов без изменения исходного кода или заголовков исходных библиотек?На самом деле мы можем.
Сначала давайте решим проблемы времени компиляции.Для этого мы окружаем заголовок, включающий кучу директив препроцессора #define
, которые ставят префикс перед всеми символами, экспортируемыми библиотекой.Позже мы сделаем это с каким-нибудь симпатичным удобным заголовком-оберткой, но просто для демонстрации того, что происходит, мы делали это дословно в исходном файле foobar.c :
example / ex02 /foobar.c
#include <stdio.h>
#define spam foo_spam
#define eggs foo_eggs
# include "foo.h"
#undef spam
#undef eggs
#define spam bar_spam
#define eggs bar_eggs
# include "bar.h"
#undef spam
#undef eggs
int main()
{
const int new_bar_spam = 3;
const double new_bar_eggs = 5.0f;
printf("foo: spam = %d, eggs = %f\n", foo_spam(), foo_eggs() );
printf("bar: old spam = %d, new spam = %d ; old eggs = %f, new eggs = %f\n",
bar_spam(new_bar_spam), new_bar_spam,
bar_eggs(new_bar_eggs), new_bar_eggs );
return 0;
}
Теперь, если мы скомпилируем это ...
example/ex02/ $ make
cc -c -o foobar.o foobar.c
cc foobar.o foo.o bar.o -o foobar
bar.o: In function `spam':
bar.c:(.text+0x0): multiple definition of `spam'
foo.o:foo.c:(.text+0x0): first defined here
bar.o: In function `eggs':
bar.c:(.text+0x1e): multiple definition of `eggs'
foo.o:foo.c:(.text+0x19): first defined here
foobar.o: In function `main':
foobar.c:(.text+0x1e): undefined reference to `foo_eggs'
foobar.c:(.text+0x28): undefined reference to `foo_spam'
foobar.c:(.text+0x4d): undefined reference to `bar_eggs'
foobar.c:(.text+0x5c): undefined reference to `bar_spam'
collect2: ld returned 1 exit status
make: *** [foobar] Error 1
... сначала кажется, что все стало хуже.Но посмотрите внимательно: на самом деле этап компиляции прошел просто отлично.Это просто компоновщик, который теперь жалуется, что встречаются символы, и он сообщает нам место (исходный файл и строку), где это происходит.И как мы видим, эти символы не имеют префикса.
Давайте посмотрим на таблицы символов с помощью утилиты nm :
example/ex02/ $ nm foo.o
0000000000000019 T eggs
0000000000000000 T spam
0000000000000008 C the_eggs
0000000000000004 C the_spams
example/ex02/ $ nm bar.o
0000000000000019 T eggs
0000000000000000 T spam
0000000000000008 C the_eggs
0000000000000004 C the_spams
Итак, перед нами стоит задачас помощью упражнения префикс этих символов в некотором непрозрачном двоичном файле.Да, я знаю, что в ходе этого примера у нас есть источники, и мы могли бы изменить это там.Но пока предположим, что у вас есть только эти .o файлы или .a (что на самом деле просто набор .o ).
objcopy на помощь
Для нас особенно интересен один инструмент: objcopy
objcopy работает с временными файлами, поэтому мыможет использовать его так, как если бы он работал на месте.Есть одна опция / операция под названием - префикс-символы , и у вас есть 3 догадки, что она делает.
Итак, давайте бросим этого парня в наши упрямые библиотеки:
example/ex03/ $ objcopy --prefix-symbols=foo_ foo.o
example/ex03/ $ objcopy --prefix-symbols=bar_ bar.o
нм показывает нам, что это похоже на работу:
example/ex03/ $ nm foo.o
0000000000000019 T foo_eggs
0000000000000000 T foo_spam
0000000000000008 C foo_the_eggs
0000000000000004 C foo_the_spams
example/ex03/ $ nm bar.o
000000000000001e T bar_eggs
0000000000000000 T bar_spam
0000000000000008 C bar_the_eggs
0000000000000004 C bar_the_spams
Давайте попробуем связать всю эту вещь:
example/ex03/ $ make
cc foobar.o foo.o bar.o -o foobar
И действительно, это сработало:
example/ex03/ $ ./foobar
foo: spam = 0, eggs = 0.000000
bar: old spam = 0, new spam = 3 ; old eggs = 0.000000, new eggs = 5.000000
Теперь я оставляю читателю в качестве упражнения реализовать инструмент / скрипт, который автоматически извлекает символы библиотеки, используя nm , записывает файл заголовка оболочки структуры
/* wrapper header wrapper_foo.h for foo.h */
#define spam foo_spam
#define eggs foo_eggs
/* ... */
#include <foo.h>
#undef spam
#undef eggs
/* ... */
и применяет префикс символа к объектным файлам статической библиотеки, используя objcopy .
А как насчет общих библиотек?
В принципе то же самое может бытьсделано с общими библиотеками.Однако общие библиотеки, как следует из названия, являются общими для нескольких программ, поэтому возиться с общей библиотекой таким способом не очень хорошая идея.
Вы не сможете обойтись написанием батутной обертки.Хуже того, вы не можете ссылаться на разделяемую библиотеку на уровне объектного файла, но вынуждены выполнять динамическую загрузку.Но это заслуживает отдельной статьи.
Оставайтесь с нами и приятного кодирования.