En azından statik kitaplıklar söz konusu olduğunda oldukça rahat bir şekilde çalışabilirsiniz.
Foo ve bar kitaplıklarının başlıklarını düşünün . Bu eğitimin iyiliği için size kaynak dosyaları da vereceğim
örnekler / ex01 / foo.h
int spam(void);
double eggs(void);
örnekler / ex01 / foo.c (bu opak olabilir / mevcut olmayabilir)
int the_spams;
double the_eggs;
int spam()
{
return the_spams++;
}
double eggs()
{
return the_eggs--;
}
örnek / ex01 / bar.h
int spam(int new_spams);
double eggs(double new_eggs);
örnekler / ex01 / bar.c (bu opak olabilir / mevcut olmayabilir)
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;
}
Bunları bir program foobarında kullanmak istiyoruz
örnek / 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;
}
Bir sorun hemen ortaya çıkıyor: C aşırı yüklemeyi bilmiyor. Yani aynı ada sahip ancak farklı imzaya sahip iki çarpı iki işlevimiz var. Bu yüzden bunları ayırt etmek için bir yola ihtiyacımız var. Her neyse, bir derleyicinin bu konuda ne söylediğini görelim:
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
Tamam, bu şaşırtıcı değildi, sadece bize zaten bildiklerimizi veya en azından şüphelendiklerimizi anlattı.
Öyleyse, orijinal kitaplıkların kaynak kodunu veya başlıklarını değiştirmeden bu tanımlayıcı çakışmasını bir şekilde çözebilir miyiz? Aslında yapabiliriz.
İlk olarak derleme zamanı sorunlarını çözelim. Bunun için, başlık #define
, kitaplık tarafından dışa aktarılan tüm sembollerin önekini içeren bir dizi önişlemci yönergesi içerir. Daha sonra bunu hoş bir sarmalayıcı başlığıyla yapıyoruz, ancak sadece neler olduğunu göstermek adına foobar.c kaynak dosyasında aynen yapıyoruz :
örnek / 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;
}
Şimdi bunu derlersek ...
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
... ilk önce işler daha da kötüye gidiyor gibi görünüyor. Ama yakından bakın: Aslında derleme aşaması gayet iyi gitti. Bu sadece şu anda çarpışan semboller olduğundan şikayet eden ve bize bunun gerçekleştiği yeri (kaynak dosya ve satır) söyleyen bağlayıcıdır. Ve gördüğümüz gibi bu semboller öneksizdir.
Nm yardımcı programıyla sembol tablolarına bir göz atalım :
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
Öyleyse şimdi opak bir ikili dosyada bu sembollerin önüne geçme alıştırması ile mücadele ediyoruz. Evet, bu örnekte kaynaklara sahip olduğumuzu ve bunu orada değiştirebileceğimizi biliyorum. Ama şimdilik, sadece bu .o dosyalarına veya bir .a'ya (aslında sadece bir grup .o ) sahip olduğunuzu varsayalım .
kurtarmaya itiraz etmek
Bizim için özellikle ilginç olan bir araç var: objcopy
objcopy geçici dosyalar üzerinde çalışır, böylece onu yerinde çalışıyormuş gibi kullanabiliriz. --Prefix-symbols adında bir seçenek / işlem vardır ve bunun ne yaptığını 3 tahmin edebilirsiniz.
Öyleyse bu adamı inatçı kütüphanelerimize atalım:
example/ex03/ $ objcopy --prefix-symbols=foo_ foo.o
example/ex03/ $ objcopy --prefix-symbols=bar_ bar.o
nm bize bunun işe yaradığını gösteriyor:
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
Tüm bu şeyi bağlamayı deneyelim:
example/ex03/ $ make
cc foobar.o foo.o bar.o -o foobar
Ve gerçekten işe yaradı:
example/ex03/ $ ./foobar
foo: spam = 0, eggs = 0.000000
bar: old spam = 0, new spam = 3 ; old eggs = 0.000000, new eggs = 5.000000
Şimdi bunu okuyucuya, nm kullanarak bir kütüphanenin sembollerini otomatik olarak çıkaran , yapının bir sarmalayıcı başlık dosyasını yazan bir araç / komut dosyası uygulama alıştırması olarak bırakıyorum.
#define spam foo_spam
#define eggs foo_eggs
#include <foo.h>
#undef spam
#undef eggs
ve sembol önekini objcopy kullanarak statik kitaplığın nesne dosyalarına uygular .
Peki ya paylaşılan kitaplıklar?
Prensipte aynısı paylaşılan kütüphanelerle de yapılabilir. Ancak, adından da anlaşılacağı gibi paylaşılan kitaplıklar birden çok program arasında paylaşılır, bu nedenle paylaşılan bir kitaplıkla bu şekilde uğraşmak o kadar da iyi bir fikir değildir.
Bir trambolin ambalajı yazmakla uğraşmayacaksınız. Daha da kötüsü, nesne dosyası düzeyinde paylaşılan kitaplığa bağlanamazsınız, ancak dinamik yükleme yapmak zorunda kalırsınız. Ama bu kendi makalesini hak ediyor.
Bizi izlemeye devam edin ve iyi kodlamalar yapın.