Al menos en el caso de las bibliotecas estáticas , puede solucionarlo con bastante comodidad.
Considere esos encabezados de bibliotecas foo y bar . Por el bien de este tutorial, también te daré los archivos fuente.
ejemplos / ex01 / foo.h
int spam(void);
double eggs(void);
examples / ex01 / foo.c (esto puede ser opaco / no disponible)
int the_spams;
double the_eggs;
int spam()
{
return the_spams++;
}
double eggs()
{
return the_eggs--;
}
ejemplo / ex01 / bar.h
int spam(int new_spams);
double eggs(double new_eggs);
examples / ex01 / bar.c (esto puede ser opaco / no disponible)
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;
}
Queremos usarlos en un programa foobar
ejemplo / 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;
}
Un problema se hace evidente de inmediato: C no conoce la sobrecarga. Entonces tenemos dos veces dos funciones con el mismo nombre pero de diferente firma. Entonces necesitamos alguna forma de distinguirlos. De todos modos, veamos qué tiene que decir un compilador sobre esto:
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
De acuerdo, esto no fue una sorpresa, solo nos dijo lo que ya sabíamos, o al menos sospechamos.
Entonces, ¿podemos de alguna manera resolver esa colisión de identificadores sin modificar el código fuente o los encabezados de las bibliotecas originales? De hecho podemos.
Primero vamos a resolver los problemas de tiempo de compilación. Para esto, rodeamos el encabezado incluye con un montón de #define
directivas de preprocesador que prefieren todos los símbolos exportados por la biblioteca. Más tarde, hacemos esto con un bonito y acogedor encabezado de envoltura, pero solo para demostrar lo que está sucediendo lo estamos haciendo textualmente en el archivo fuente foobar.c :
ejemplo / 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;
}
Ahora, si compilamos esto ...
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
... primero parece que las cosas empeoraron. Pero mira de cerca: en realidad, la etapa de compilación fue bien. Es solo el enlazador el que ahora se queja de que hay símbolos que chocan y nos dice la ubicación (archivo fuente y línea) donde ocurre esto. Y como podemos ver, esos símbolos no tienen prefijo.
Echemos un vistazo a las tablas de símbolos con la utilidad 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
Así que ahora tenemos el desafío del ejercicio de anteponer esos símbolos en algún binario opaco. Sí, sé que en el transcurso de este ejemplo tenemos las fuentes y podría cambiar esto allí. Pero por ahora, asuma que solo tiene esos archivos .o , o un .a (que en realidad es solo un montón de .o ).
objcopy al rescate
Hay una herramienta especialmente interesante para nosotros: objcopy
objcopy funciona en archivos temporales, por lo que podemos usarlo como si estuviera operando en el lugar. Hay una opción / operación llamada --prefix-symbols y tiene 3 suposiciones de lo que hace.
Así que arrojemos a este amigo a nuestras obstinadas bibliotecas:
example/ex03/ $ objcopy --prefix-symbols=foo_ foo.o
example/ex03/ $ objcopy --prefix-symbols=bar_ bar.o
nm nos muestra que esto parecía funcionar:
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
Intentemos vincular todo esto:
example/ex03/ $ make
cc foobar.o foo.o bar.o -o foobar
Y de hecho, funcionó:
example/ex03/ $ ./foobar
foo: spam = 0, eggs = 0.000000
bar: old spam = 0, new spam = 3 ; old eggs = 0.000000, new eggs = 5.000000
Ahora dejo como ejercicio para el lector implementar una herramienta / script que extrae automáticamente los símbolos de una biblioteca usando nm , escribe un archivo de encabezado contenedor de la estructura
#define spam foo_spam
#define eggs foo_eggs
#include <foo.h>
#undef spam
#undef eggs
y aplica el prefijo de símbolo a los archivos de objeto de la biblioteca estática mediante objcopy .
¿Qué pasa con las bibliotecas compartidas?
En principio, se podría hacer lo mismo con las bibliotecas compartidas. Sin embargo, las bibliotecas compartidas, el nombre lo indica, se comparten entre varios programas, por lo que jugar con una biblioteca compartida de esta manera no es una buena idea.
No podrás escribir un envoltorio de trampolín. Peor aún, no puede vincular con la biblioteca compartida a nivel de archivo de objeto, pero se ve obligado a realizar una carga dinámica. Pero esto merece su propio artículo.
Estén atentos y feliz codificación.