DLLExport und visibility default
« | 01 May 2020 | »So, die Umstellung läuft und meine Finger dampfen.
In Zukunft bekommt das GATE Projekt die Fähigkeit, die Core-Libs dynamisch
zu erstellen und zu linken.
Und jetzt könnte ich mich grün und blau ärgern, dass ich erst nach dem Hinzufügen von etwa 600 Makros feststellte, dass sie an der falschen standen ….
Vorgeschichte
Das GATE wird für mich immer eine statische Bibliothek bleiben und das ist auch gut so. Doch auf Grund seiner nun doch schon recht zahlreichen Funktionen (alleine 600 in der Core-Lib - also ohne IO, System, usw.) kann man durchaus überdenken, diese “Features” auch dynamisch von einer DLL/SO importierbar zu machen.
Nun, dank CMake ist das alles kein
Problem, man definiert die Option
BUILD_SHARED_LIBS
und entfernt bei
add_library(my_lib_name STATIC ...)
einfach das STATIC
.
Denn wieder was gelernt: BUILD_SHARED_LIBS
fügt automatisch bei allen
add_library
Funktionen ein SHARED
ein während ansonsten STATIC
als
Standard angenommen wird.
Aber die eigentlich interessanteste Frage des Tages lautet:
Steht jetzt
__declspec(dllexport)
vor der Funktionsdeklaration oder zwischen Return-Value und Namen?
Die einfache Antwort: It depends … also mal so und mal so.
Nachdem ich selbst schon länger keine DLLs mehr erstellt habe, hab ich einfach in den ZLIB Source hineingeblickt und dort die zweite Variante gefunden … bzw. ich dachte, ich hätte die zweite Variante gesehen.
Also wurden brav Macros definiert, die entweder auf dllexport
oder
dllimport
ausgelegt waren und anschließend alle Funktionen der GATE Corelib
auf die Signatur:
ret_type_t GATE_CORE_API some_function(arg_t arg, ...);
umgestellt.
Ein kurzer Test nach den ersten paar Funktionen verlief natürlich mit Erfolg, wie sich später herausstellte war er falsch konfiguriert.
So, 600 Funktionen umgestellt und da kam der hier hunderte Male:
error C2059: syntax error: 'type'
Portable Lösung
Wie es aussieht, sind die Toleranzen für C und C++ Projekte im
MSVC etwas unterschiedlich.
Während man bei C++ Übersetzungseinheiten durchaus
ret_type_t GATE_CORE_API some_function(arg_t arg, ...);
nutzen darf, sollte man auch laut Empfehlung auf der MSDN
GATE_CORE_API ret_type_t some_function(arg_t arg, ...);
benützen.
Das macht, wenn man darüber nachdenkt, auch Sinn.
Und wie sieht es beim GCC unter POSIX aus?
Ein __declspec(dllexport)
entspräche hier einem
__attribute__((visibility ("default")))
, wenn man mit die Übersetzung mit
dem Parameter -fvisibility=hidden
ausführt. Ohne die Default-Visibility
hidden
würde der GCC alles exportieren was kein static
davor hat,
und damit auch einiges was privat bleiben sollte
(z.B. nicht statische Klassenmethoden).
Der dllimport
hat in dem Scenario kein Äquivalent und wird durch ein leeres
Makro auf Linux portiert.
Und obwohl die GCC Dokumentation zur Variante
ret_type_t GATE_CORE_API some_function(arg_t arg, ...);
rät, so akzeptiert der Compiler auch die Alternative.
Nun gut, die finale Lösung heißt also:
GATE_CORE_API ret_type_t some_function(arg_t arg, ...);
Und so habe ich die Projekt Konvertierung auch weiter durchgeführt.
HIER gibt es übrigens noch eine tolle Zusammenfassung zum Thema GCC-Visibility.
Und die perfekte Lösung?
Um wieder zum Anfang zurück zu kommen: Eine annähernd perfekte Lösung
setzt die ZLIB
ein.
Sie nutzt nämlich zwei Makros, eines vorne und eines in der Mitte:
1ZEXTERN const char * ZEXPORT zlibVersion OF((void));
Und hier kam mein Irrtum zustande, denn ich dachte hinter ZEXPORT
verbirgt sich der dllexport
und tatsächlich war diese Variante für
das Betriebssystem BEOS mal gültig, doch bei den MSVC Zweigen sieht
man deutlich, dass in Wahrheit ZEXTERN
den Export übernimmt und
ZEXPORT
leer bleibt.
Ich habe wieder was gelernt:
Unsere Vielzahl an Compilern hat offenbar eine Vielzahl an Möglichkeiten
geschaffen Export-Schlüsselworte einzusetzen. Da braucht man wohl ein
paar Optionen mehr, damit man jede erdenkliche Spielerei abfangen kann.
Wie auch immer … für meine Zwecke reicht mir meine Erkenntnis. Und nachdem dynamische Libs keine hohe Priorität haben, kann ich das Thema vorerst wieder auf Eis legen.