Erstellen einer API-Library



  • Hallo,

    ich habe ein Programm mit zahlreichen Klassen.
    Jetzt möchte ich dem Benutzer eine Schnittstellen-API zur Verfügung stellen
    um eine eigene Library zu erstellen.
    Von einigen der Klassen möchte ich es ihm erlauben abzuleiten.
    Der Benutzer soll mit Hilfe einer dynamischen 'API-Library' seine eigene
    dynamische 'Benutzer-Library' linken können.
    Jetzt stehe ich vor folgendem Problem:
    Die 'API-Library' muss ja die Implementierungen der Basis-Klassen enthalten(?),
    da der Linker sonst beim Erstellen der 'Benutzer-Library' meckert.
    In den Implementierungen der Basisklasse werden jedoch andere Klassen verwendet,
    so dass ich diese beim Linken der 'API-Library' auch Informationen dieser
    Klassen haben muss. Jetzt möchte ich die API-Library jedoch möglichst schlank
    halten.
    Wie krieg ich das am cleversten hin?
    Im Moment erstelle ich die Applikation selbst als ausführbare Datei.
    Diese soll (wenn möglich) ein Binary ohne Library-Abhängigkeiten bleiben.
    Dann erstelle ich eine dynamische Library mit allen Klassen.
    Diese benötige ich nur um die 'API-Library' zu erstellen um keine Probleme mit
    dem Linker zu kriegen.
    Diese schlanke 'API-Library' kann ich nun zum Erstellen der 'Benutzer-Library'
    erstellen.

    Also eigentlich klappt das ganz gut.
    Gibt es nicht einen Weg ohne die 'Hilfs-Library' mit allen Klassen?

    Ich verwende g++ (GCC) 4.5.2 und benutze folgende Anweisungen zum
    erstellen der Libraries:

    g++ -shared -o ../dist/libmyimpl.so $(OBJS) $(LIBS)
    g++ -shared -o libmyapi.so $(APIOBJS) -L../dist/ -lmyimpl $(LIBS)
    

    Gibt es einen Weg ohne die 'Hilfs-Library'?

    Gruß,
    Mark



  • Könntest du mal bitte ein minimalistisches Code-Beispiel posten? Wären eventuell Templates etwas für dich?



  • Hallo wxSkip,
    mit Templates bin ich eigentlich vertraut und die bringen mich wohl nicht weiter.

    Okay...
    Ich gehe das ganze Szenario mal anhand eines Minimal-Beispiels durch.
    (Auch wenn es trotzdem ziemlich lang ist)

    Also ich habe eine API-Klasse:

    apifoo.hpp:

    class ApiFoo{
            virtual void foo() = 0;
            virtual void foobar();
    };
    

    Der Benutzer der Library soll jetzt von dieser Klasse erben können.

    userfoo.hpp:

    #include "apifoo.hpp"
    
    class UserFoo : public ApiFoo{
    public:
            virtual void foo();
    };
    

    userfoo.hpp:

    #include "userfoo.hpp"
    
    void UserFoo::foo(){
    }
    

    Jetzt benötigt der Linker ja beim Erstellen der Library Informationen der Klasse ApiFoo:

    g++ -shared -o libuser.so userfoo.cpp
    
    Undefined symbols:
      "typeinfo for ApiFoo", referenced from:
          typeinfo for UserFoo in cckiGIo3.o
      "ApiFoo::foobar()", referenced from:
          vtable for UserFoo in cckiGIo3.o
    ld: symbol(s) not found
    collect2: ld returned 1 exit status
    

    Diese Informationen möchte ich ihm in Form einer API-Library zur Verfügung stellen:

    apifoo.hpp:

    class ApiFoo{
            virtual void foo() = 0;
            virtual void foobar();
    };
    
    g++ -shared -o libapi.so apifoo.cpp
    

    Jetzt kompiliert er mit dieser Library-Information auch einwandfrei:

    g++ -shared -o libuser.so userfoo.cpp -lapi -L.
    

    Wenn jedoch ApiFoo in der Implementierung andere Klassen verwendet,

    apifoo.cpp:

    #include "apifoo.hpp"
    #include "bar.hpp"
    
    void ApiFoo::foobar(){
            Bar bar;
            bar.bar();
    }
    

    bar.hpp:

    class Bar {
    public:
            void bar();
    };
    

    bar.cpp:

    #include "bar.hpp"
    
    void Bar::bar(){
    }
    

    funktioniert das Linken der Api-Library nicht mehr

    g++ -shared -o libapi.so apifoo.cpp
    
    Undefined symbols:
      "Bar::bar()", referenced from:
          ApiFoo::foobar()     in ccmvPVrL.o
    ld: symbol(s) not found
    collect2: ld returned 1 exit status
    

    Jetzt möchte ich aber nicht alle Klassen und deren Abhängigkeiten in der API-Library haben,
    weswegen Folgendes für mich nicht in Frage kommt:

    g++ -shared -o libapi.so apifoo.cpp bar.cpp
    

    Deswegen builde ich mir eine mir eine Hilfs-Library, nur zum Linken der API-Library.
    Diese enthält alle existierenden Klassen. Ich weiß, alle (rekursiv) abhängigen würden ausreichen.
    Hier enthält diese wegen der Reduktion auf das Minimalbeispiel nur die Klasse Bar:

    g++ -shared -o libhelp.so bar.cpp
    

    Nun kann ich mir meine API-Library bauen:

    g++ -shared -o libapi.so apifoo.cpp -lhelp -L.
    

    Diese kann der Benutzer dann zum bauen seiner Library benutzen:

    g++ -shared -o libuser.so userfoo.cpp -lapi -L.
    

    Folglich lautet meine Befehlsreihenfolge:

    g++ -shared -o libhelp.so bar.cpp
    g++ -shared -o libapi.so apifoo.cpp -lhelp -L.
    g++ -shared -o libuser.so userfoo.cpp -lapi -L.
    

    Was mich daran stört ist, dass ich die libhelp.so nur benötige fürs
    Bauen der API-Library. Danach wird sie nie wieder verwendet, da die eigentliche Applikation,
    die sich später die User-Library läd, alle Klassen im Binary beinhaltet.

    Wie würdet ihr bei dem Szenario vorgehen?

    Ich hoffe mein Problem ist jetzt klar(er) geworden.

    Gruß,
    Mark



  • Warum tust du bar.cpp nicht einfach zu deiner libapi.so dazu, wenn du die Klasse Bar sowieso im Code verwendest?



  • wxSkip schrieb:

    Warum tust du bar.cpp nicht einfach zu deiner libapi.so dazu, wenn du die Klasse Bar sowieso im Code verwendest?

    Ich möchte halt eine Library mit den nötigsten Klassen. Außerdem bräuchte ich so
    nicht nur bar.cpp, sondern alle rekursiv abhängigen Klassen. Das wäre ein zusätzlicher ziemlich blöder Aufwand zu kucken, welche Teilmenge es genau ist.
    Im Moment ist meine API 53KB groß, während meine helper Library 668KB
    groß ist.



  • Ohne diese Helper funktioniert deine Library aber nicht. Entweder, du modularisierst deine Library, sodass nur ein spezieller Teil die Libraries braucht, den der User dann nur linken braucht, wenn er dieses Modul deiner Library braucht. Wenn du aber alles linken willst, dann musst du auch alles linken, was dazu gehört.


Anmelden zum Antworten