Принаймні у випадку зі статичними бібліотеками ви можете обійти це досить зручно.
Розглянемо ті заголовки бібліотек foo and bar . Для цього підручника я також дам вам вихідні файли
приклади / 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--;
}
example / 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 працює з тимчасовими файлами, тому ми можемо використовувати його так, ніби він працює на місці. Є одна опція / операція, яка називається --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
Тепер я залишаю читачеві вправу реалізувати інструмент / сценарій, який автоматично витягує символи бібліотеки за допомогою nm , пише файл заголовка обгортки структури
#define spam foo_spam
#define eggs foo_eggs
#include <foo.h>
#undef spam
#undef eggs
і застосовує префікс символу до об’єктних файлів статичної бібліотеки за допомогою objcopy .
А як щодо спільних бібліотек?
В принципі, те ж саме можна зробити із спільними бібліотеками. Однак спільні бібліотеки, як це випливає з назви, спільно використовуються між різними програмами, тому возитися із спільною бібліотекою таким чином не є такою гарною ідеєю.
Ви не обійдетеся, написавши батутну обгортку. Ще гірше, що ви не можете встановити зв'язок із спільною бібліотекою на рівні файлу об'єкта, але змушені робити динамічне завантаження. Але це заслуговує на власну статтю.
Залишайтеся з нами, і щасливого кодування.