Zumindest bei statischen Bibliotheken können Sie dies ganz bequem umgehen.
Betrachten Sie die Überschriften der Bibliotheken foo und bar . Für dieses Tutorial gebe ich Ihnen auch die Quelldateien
Beispiele / ex01 / foo.h.
int spam(void);
double eggs(void);
examples / ex01 / foo.c (dies ist möglicherweise undurchsichtig / nicht verfügbar)
int the_spams;
double the_eggs;
int spam()
{
return the_spams++;
}
double eggs()
{
return the_eggs--;
}
Beispiel / ex01 / bar.h.
int spam(int new_spams);
double eggs(double new_eggs);
Beispiele / ex01 / bar.c (dies ist möglicherweise undurchsichtig / nicht verfügbar)
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;
}
Wir wollen diese in einer Programm-Foobar verwenden
Beispiel / 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;
}
Ein Problem wird sofort deutlich: C kennt keine Überladung. Wir haben also zwei mal zwei Funktionen mit identischem Namen, aber unterschiedlicher Signatur. Wir brauchen also einen Weg, um diese zu unterscheiden. Wie auch immer, mal sehen, was ein Compiler dazu zu sagen hat:
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
Okay, das war keine Überraschung, es hat uns nur gesagt, was wir bereits wussten oder zumindest vermuteten.
Können wir diese Identifikatorkollision also irgendwie beheben, ohne den Quellcode oder die Header der Originalbibliotheken zu ändern? In der Tat können wir.
Lassen Sie uns zunächst die Probleme mit der Kompilierungszeit beheben. Zu diesem Zweck umgeben wir die Header-Includes mit einer Reihe von Präprozessor- #define
Direktiven, die allen von der Bibliothek exportierten Symbolen vorangestellt sind. Später machen wir das mit einem netten, gemütlichen Wrapper-Header, aber nur um zu demonstrieren, was los ist, haben wir es wörtlich in der foobar.c- Quelldatei gemacht:
Beispiel / 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;
}
Nun, wenn wir dies kompilieren ...
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
... es sieht zunächst so aus, als wäre es schlimmer geworden. Aber schauen Sie genau hin: Eigentlich lief die Kompilierungsphase ganz gut. Es ist nur der Linker, der sich jetzt darüber beschwert, dass Symbole kollidieren, und der uns den Ort (Quelldatei und Zeile) mitteilt, an dem dies geschieht. Und wie wir sehen können, sind diese Symbole nicht fixiert.
Schauen wir uns die Symboltabellen mit dem Dienstprogramm nm an :
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
Jetzt müssen wir diese Symbole in einer undurchsichtigen Binärdatei voranstellen. Ja, ich weiß im Verlauf dieses Beispiels, dass wir die Quellen haben und dies dort ändern könnten. Aber für den Moment nehmen Sie einfach an, dass Sie nur diese .o- Dateien oder eine .a-Datei haben (was eigentlich nur ein Haufen von .o ist ).
Objekt zur Rettung
Es gibt ein Werkzeug, das für uns besonders interessant ist: die Objektkopie
objcopy funktioniert mit temporären Dateien, sodass wir es so verwenden können, als ob es direkt funktioniert. Es gibt eine Option / Operation namens --prefix-Symbole und Sie haben 3 Vermutungen, was es tut.
Also werfen wir diesen Kerl auf unsere hartnäckigen Bibliotheken:
example/ex03/ $ objcopy --prefix-symbols=foo_ foo.o
example/ex03/ $ objcopy --prefix-symbols=bar_ bar.o
nm zeigt uns, dass dies zu funktionieren schien:
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
Versuchen wir, diese ganze Sache zu verknüpfen:
example/ex03/ $ make
cc foobar.o foo.o bar.o -o foobar
Und tatsächlich hat es funktioniert:
example/ex03/ $ ./foobar
foo: spam = 0, eggs = 0.000000
bar: old spam = 0, new spam = 3 ; old eggs = 0.000000, new eggs = 5.000000
Jetzt überlasse ich es dem Leser als Übung, ein Tool / Skript zu implementieren, das die Symbole einer Bibliothek automatisch mit nm extrahiert und eine Wrapper-Header-Datei der Struktur schreibt
/* wrapper header wrapper_foo.h for foo.h */
#define spam foo_spam
#define eggs foo_eggs
/* ... */
#include <foo.h>
#undef spam
#undef eggs
/* ... */
und wendet das Symbolpräfix mithilfe von objcopy auf die Objektdateien der statischen Bibliothek an .
Was ist mit gemeinsam genutzten Bibliotheken?
Im Prinzip könnte das gleiche mit gemeinsam genutzten Bibliotheken gemacht werden. Wie der Name schon sagt, werden gemeinsam genutzte Bibliotheken von mehreren Programmen gemeinsam genutzt. Daher ist es keine gute Idee, auf diese Weise mit einer gemeinsam genutzten Bibliothek herumzuspielen.
Sie werden nicht darum herumkommen, eine Trampolinverpackung zu schreiben. Schlimmer noch, Sie können keine Verknüpfung mit der gemeinsam genutzten Bibliothek auf der Ebene der Objektdatei herstellen, müssen jedoch dynamisch laden. Dies verdient jedoch einen eigenen Artikel.
Bleiben Sie dran und viel Spaß beim Codieren.