Ответ 1
По крайней мере, в случае статических библиотек вы можете обходиться довольно удобно.
Рассмотрим те заголовки библиотек foo и bar. Ради этого урока я также расскажу о исходных файлах
примеры /ex 01/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--;
}
Пример /ex 01/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
Пример /ex 01/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:
Пример /ex 02/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 работает во временных файлах, поэтому мы можем использовать его, как если бы он работал на месте. Есть один option/operation, называемый --prefix-symbols, и у вас есть 3 догадки о том, что он делает.
Итак, бросьте этого парня на наши упрямые библиотеки:
example/ex03/ $ objcopy --prefix-symbols=foo_ foo.o
example/ex03/ $ objcopy --prefix-symbols=bar_ bar.o
nm показывает нам, что это сработало:
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
Теперь я оставляю это как упражнение для чтения для реализации инструмента /script, который автоматически извлекает символы библиотеки, использующей 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.
Что относительно разделяемых библиотек?
В принципе то же самое можно сделать с общими библиотеками. Однако общие библиотеки, это имя говорит об этом, разделяются между несколькими программами, поэтому беспорядок с общей библиотекой таким образом не является такой хорошей идеей.
Вам не обойтись написанием батутной обертки. Хуже того, вы не можете подключиться к общей библиотеке на уровне объектного файла, но вынуждены выполнять динамическую загрузку. Но это заслуживает своей собственной статьи.
Оставайтесь с нами, и счастливое кодирование.