DLLExport und visibility default

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.