Jakiś czas temu pobrałem źródła do VTTEST https://invisible-island.net/vttest/#download i zacząłem je przerabiać na swoje potrzeby
Niedawno potrzebowałem zaemulować system z Z80 bazując na https://github.com/Jean-MarcHarvengt/MCUME/tree/master/MCUME_pico/pico81 i pracujący na płytce Picomputer.
Teraz nie jestem w stanie "na poczekaniu" umieścić kawałka kodu obrazującego problem, ale spróbuję go przedstawić własnymi słowami. Temat nie jest już aktualny, bo w tych przypadkach udało mi się zrobić to co chciałem inaczej i większym nakładem pracy, jednak aktualne pozostaje pytanie, o to, jak działa kompilacja i z czego wynika problem.
Oryginalne źródła bez problemu kompilują się w oryginalnej postaci. VTTest kompiluje się za pomoca standardowego 'configure' i 'make', a Pico81 kompiluje się w sposób opisany w readme projektu.
Jednak nie o tym mowa. W obu projektach chciałem dorobić strukturę i funkcje bardzo globalne, dostępne z każdego miejsca, czy z wielu miejsc. Niekoniecznie jest to najlepsze rozwiązanie, ale jest to najprostsze do zrobienia. Próbowałem w następujący sposób: Tworzę nowy plik, powiedzmy additional.h
, w tym pliku umieszczam taką treść (najprościej, jak można):
#ifndef ADDITIONAL_H
#define ADDITIONAL_H
int SomeGlobalVariable = 0;
void SomeGlobalMethod()
{
}
#endif
Potem we wszystkich plikach źródła, w których chce mieć dostęp do globalnej zmiennej, umieszczam najzwyczajniej w świecie:
#include "additional.h"
Ja rozumiem to tak, że preprocesor w miejsce każdego #include
wstawia treść wskazanego pliku, a jak są podane kilka plików źródłowych, jak przykład poniżej, to następuje konkatenacja tekstów z podanych plików
gcc file1.cpp file2.cpp file3.cpp -o binaryfile
Z tego, co rozumiem, pierwsza, druga i ostatnia linia przykładowego pliku additional.h
sprawia, że przy pierwszym wystąpieniu #include "additional.h"
zostanie wstawiony tekst z tego pliku, a za każdym następnym wystąpieniem, nic nie zostanie wstawione, bo zdefiniowanie ADDITIONAL_H
zablokuje przetworzenie treści objętej dyrektywami #ifndef ADDITIONAL_H
i #endif
. Z tego, co czytałem, ta dyrektywa to jest popularna technika sprawiania, że dany plik zostanie wstawiony tylko jeden raz, w miejsce pierwszego wystąpienia #include "additional.h"
. Niektóre IDE same generują te dyrektywy przy tworzeniu nowych plików klas.
W ten oto sposób, przed samą kompilacją jest jeden wielki tekst kodu źródłowego, który już nie zawiera dyrektyw. Jak sama nazwa mówi, preprocesor, to jest wstępne przetwarzanie przygotowawcze niebędące kompilowaniem, mające miejsce przed zasadniczym przetwarzaniem, jakim jest kompilacją.
Czy to wszystko, co powyżej napisałem odnośnie działania dyrektyw jest prawdą i czy dobrze rozumiem?
Jeżeli takie rzeczy robię w swoich projektach, które robię od zera, to nie ma żadnego problemu, kompilacja programu przebiega bez problemu, a program działa zgodnie z moimi wyobrażeniami.
Natomiast, jak coś robię w cudzym projekcie, co miało miejsce w dwóch wymienionych, to pojawia się problem. Problem jest taki, że kompilator podaje komunikat, że zmienna SomeGlobalVariable
jest już zadeklarowana, podobnie, jak metoda SomeGlobalMethod
jest już wcześniej zaimplementowana. Wygląda, jakby dyrektywy nie działały, lub jakby kompilator przy dalszym przetwarzaniu zapominał, że wystąpił #define ADDITIONAL_H
i później mimo tego, ponownie wstawia zawartość pliku, co oczywiście skutkuje błędem kompilacji.
Co do "mojego" C++, to jeżeli cos robię dla okienek w Qt creator, to dodaję kolejne pliki, nie zawracając sobie głowy konfiguracją kompilowania. Jeżeli coś robię w WASM, to kompiluję poleceniem emcc file1.cpp file2.cpp file3.cpp -o outscript.js
Czym różni się kompilowanie cudzych projektów od mojego, że przy moim projekcie, jak chce dorobić coś globalnego to nie ma żadnego problemu (a to, czy to jest rozsądne posunięcie to już inna sprawa, ale to nie o tym jest ten wątek), a jak w cudzym projekcie, to jest problem z działaniem dyrektyw i wielokrotnym dołączeniem tego samego pliku?
Wygląda na to, że kompilowanie tych projektów według dokumentacji przebiega inaczej i przez to kompilator gubi informację o dyrektywie, a i tak wychodzi jeden plik binarny. Z czego to wynika?
-E
, ja bym najpierw skompilował bez modyfikacji i potem dodawał po trochu, aż się wywali i analizował od tego miejsca. Też może być tak, że każdy plik jest oddzielnie kompilowany i potem linker je łączy, to dopiero wtedy wyjdzie, że kilka plików ma te same symbole w tablicy, nie wiem czy to może być zależne od wersji kompilatora.