shared objects und polymorphie



  • (war mir nich ganz sicher ob Linux/Unix oder Andere Compiler, aber mir wurde Linux/Unix empfohlen)

    Ich hab ein Projekt, das bisher in 2 Komponenten geteilt is: User Interace und Code. Der Code ist in einer .so versteckt. Nun verwende ich Polymorphie um späteren mit dlopen eingebundenen .so s das Leben zu erleichtern. (Zugegeben, die Architektur ist nich die einfachste).
    Nun krieg ich folgende Fehler:

    g++ -Wall -W -g -O0 -fPIC -L. -lcalculator -o calculator main.o
    ./libcalculator.so: undefined reference to `typeinfo for environment'
    ./libcalculator.so: undefined reference to `vtable for environment'
    collect2: ld returned 1 exit status
    make: *** [calculator] Fehler 1
    

    Was das bedeuten soll ist mir schleierhaft.

    Die Datei environment.h:

    #ifndef ENVIRONMENT_H
    #define ENVIRONMENT_H
    #include <string>
    #include "type.h"
    
    class environment {
    public:
        virtual ~environment() = 0;
        virtual int register_type(const std::string &, factory) = 0;
        virtual int tid(const std::string &) const = 0;
        virtual object create_object(int) const = 0;
        virtual object create_object(const std::string &) const = 0;
        virtual void *alloc(unsigned long) const;
        virtual void free(void *) const;
    };
    
    inline environment::~environment() {}
    #endif
    

    Und schließlich noch die relevanten Teile des Makefiles:

    CXXFLAGS=-Wall -W -g -O0 -fPIC
    CXX=g++
    cxx=$(CXX) $(CXXFLAGS)
    
    all: calculator 
    
    calculator: main.o libcalculator.so
        $(cxx) -L. -lcalculator -o calculator main.o
    
    main.o: main.cpp calc.h
        $(cxx) -o main.o -c main.cpp
    
    libcalculator.so: calc.o tokenizer.o type.o environment.o
        $(cxx) -shared -o libcalculator.so calc.o type.o environment.o
    
    calc.o: calc.cpp calc.h
        $(cxx) -o calc.o -c calc.cpp
    
    tokenizer.o: tokenizer.cpp tokenizer.h
        $(cxx) -o tokenizer.o -c tokenizer.cpp
    
    type.o: type.cpp type.h
        $(cxx) -o type.o -c type.cpp
    
    environment.o: environment.cpp environment.h environment_impl.h
        $(cxx) -o environment.o -c environment.cpp
    
    [...]
    

    Hat jemand eine Ahnung?

    Ach ja: ich habe eine Klasse environment_impl, die von environment erbt. Diese wird in einer Datei (calc.cpp) instanziiert.



  • Ich glaub ich weiß jetz zumindest, wo der Fehler liegt:
    evironment_impl.h:

    #ifndef ENVIRONMENT_IMPL_H
    #define ENVIRONMENT_IMPL_H
    #include "environment.h"
    #include <map>
    #include <vector>
    
    class environment_impl /*: public environment*/ {
        std::map<std::string, int> tids;
        std::vector<factory> types;
    public:
        environment_impl();
        virtual ~environment_impl();
        virtual int register_type(const std::string &, factory);
        virtual int tid(const std::string &) const;
        virtual object create_object(int) const;
        virtual object create_object(const std::string &) const;
        virtual void *alloc(unsigned long) const;
        virtual void free(void *) const;
    };
    #endif
    

    dieses /: public environment/ war vorher nicht auskommentiert. nur, jetzt machts so keinen sinn. mal weiterschaun.



  • etwas modifiziert krieg ich diese fehler:

    /tmp/ccnJ5IZT.o(.gnu.linkonce.t._ZN11environmentC2Ev+0x8): In function `environment::environment[not-in-charge]()':
    : undefined reference to `vtable for environment'
    /tmp/ccnJ5IZT.o(.gnu.linkonce.d._ZTI16environment_impl+0x10): undefined reference to `typeinfo for environment'
    

    *argh* 😞



  • @Moderatoren: Bitte nach C++ verschieben. Der letzte Test zeigt, dass es nichts mit shared objects zu tun hat.



  • okay



  • @Mr.N
    Du scheinst hier über ein klassisches gemeines Implementationsdetail gestolpert zu sein.

    Das Problem (und seine Lösung) habe ich hier beschrieben: Wegen Konstruktor cpp-File anlegen?



  • jetzt mal alle dateien unter libcalculator.so:
    (sorry, aber ich bin verzweifelt)

    type.h:

    #ifndef TYPE_H
    #define TYPE_H
    #include <string>
    
    class type;
    
    class refcounted_ptr {
        struct internal {
            unsigned long count;
            type *p;
        } *obj;
    public:
        refcounted_ptr();
        refcounted_ptr(type *);
        refcounted_ptr(const refcounted_ptr &);
        ~refcounted_ptr();
        type *operator *() const;
        void operator=(const refcounted_ptr &);
    };
    
    class object {
        refcounted_ptr obj;
    public:
        object(type *);
        ~object();
    };
    
    class type {
    public:
        virtual ~type() = 0;
        virtual void init(void *, unsigned long = 4) = 0;
        virtual void destruct() = 0;
        virtual object convert(const std::string &) const = 0;
        virtual object convert(int) const = 0;
        virtual int tid() const = 0;
        virtual std::string tstr() const = 0;
    };
    
    typedef type *(*factory)();
    #endif
    

    environment.h:

    #ifndef ENVIRONMENT_H
    #define ENVIRONMENT_H
    #include <string>
    #include "type.h"
    
    class environment {
    public:
        environment() {}
        virtual ~environment() {}
        virtual int register_type(const std::string &, factory) = 0;
        virtual int tid(const std::string &) const = 0;
        virtual object create_object(int) const = 0;
        virtual object create_object(const std::string &) const = 0;
        virtual void *alloc(unsigned long) const;
        virtual void free(void *) const;
    };
    #endif
    

    environment_impl.h:

    #ifndef ENVIRONMENT_IMPL_H
    #define ENVIRONMENT_IMPL_H
    #include "environment.h"
    #include <map>
    #include <vector>
    
    class environment_impl : public environment {
        std::map<std::string, int> tids;
        std::vector<factory> types;
    public:
        environment_impl();
        virtual ~environment_impl();
        virtual int register_type(const std::string &, factory);
        virtual int tid(const std::string &) const;
        virtual object create_object(int) const;
        virtual object create_object(const std::string &) const;
        virtual void *alloc(unsigned long) const;
        virtual void free(void *) const;
    };
    #endif
    

    calc.h:

    #ifndef CALC_H
    #define CALC_H
    #include <string>
    class calc {
    public:
        calc();
        ~calc();
        std::string evaluate(const std::string &);
    };
    #endif
    

    calc.cpp:

    #include "calc.h"
    
    #include "environment_impl.h"
    
    //environment_impl x;
    //environment *env = &x;
    
    using namespace std;
    
    calc::calc() {
    //  env->register_type("bla", (factory) 0);
    }
    
    calc::~calc() {
    
    }
    
    string calc::evaluate(const string &s) {
        return s;
    }
    

    environment.cpp:

    #include "environment_impl.h"
    
    #include <cstdlib>
    
    using namespace std;
    
    namespace {
        unsigned long roundup(unsigned long i) {
            return i + (32768 - i % 32768);
        }
    }
    
    environment_impl::environment_impl() {
    }
    
    environment_impl::~environment_impl() {
    }
    
    int environment_impl::register_type(const string &key, factory value) {
        types.reserve(roundup(types.size() + 1));
        types.resize(types.size() + 1);
        types[types.size() - 1] = value;
        tids.insert(pair<string, int>(key, types.size() - 1));
        return types.size() - 1;
    }
    
    int environment_impl::tid(const string &key) const {
        return tids.find(key)->second;
    }
    
    object environment_impl::create_object(int i) const {
        return object((*types[i])());
    }
    
    object environment_impl::create_object(const string &key) const {
        return object((*types[tid(key)])());
    }
    
    void *environment_impl::alloc(unsigned long len) const {
        return std::malloc(len);
    }
    
    void environment_impl::free(void *p) const {
        return std::free(p);
    }
    
    //environment::~environment() {}
    

    type.cpp:

    #include "type.h"
    
    refcounted_ptr::refcounted_ptr() : obj(0) {
    }
    
    refcounted_ptr::refcounted_ptr(type *p) : obj(new internal) {
        obj->count = 1;
        obj->p = p;
    }
    
    refcounted_ptr::refcounted_ptr(const refcounted_ptr &b) : obj(b.obj) {
        ++obj->count;
    }
    
    refcounted_ptr::~refcounted_ptr() {
        if (--obj->count == 0)
            delete obj;
    }
    
    type *refcounted_ptr::operator *() const {
        return obj->p;
    }
    
    void refcounted_ptr::operator=(const refcounted_ptr &b) {
        if (--obj->count == 0)
            delete obj;
        obj = b.obj;
        ++obj->count;
    }
    
    object::object(type *p) : obj(p) {
    }
    
    object::~object() {
    }
    

    tokenizer.h und tokenizer.cpp sind ohne jeden belang (leer)



  • @HumeSikkins: und das sagst du, kurz nachdem ich den letzten post begonnen habe :D. naja, mal anschaun, was du da schreibst.



  • Hallo,
    hast du den Text gelesen? Und hast du den Destruktor von enivronment (nicht environment_impl) mal in der cpp-Datei?



  • der fehler scheint behoben *freu*. muss ich eigentlich den destruktor einer abstrakten klasse implementieren? wenn ja, WOZU?



  • muss ich eigentlich den destruktor einer abstrakten klasse implementieren?

    Ja. Ein Destruktor muss *immer* implementiert werden. Selbst wenn er pure virtual ist.

    wenn ja, WOZU?

    Auch eine abstrakte Klasse kann Datenelemente enthalten bzw. von einer Klasse mit Datenelementen erben. Und diese Datenelemte können Destruktoren besitzen. Aus diesem Grund werden auch Destruktoren von abstrakten Klassen aufgerufen, wenn Objekte von Klassen die von diesen abstrakten Klassen erben zerstört werden.
    Selbst wenn du im Dtor der abstrakten Klasse nichts tust, muss dort trotzdem zur Not Code eingebaut werden, der Basisklassen/Elementdestruktoren aufruft.



  • hmpf. ich habe aber keine daten-elemente. naja, muss wohl sein.



  • gehe ich eine gefahr ein, wenn ich diesen destruktor komplett weglasse?



  • gehe ich eine gefahr ein, wenn ich diesen destruktor komplett weglasse?

    Sicher. Implizit erzeugte Destruktoren sind nicht virtuell. Wenn du also in einer Basisklasse nicht explizit einen virtuellen Destruktor anlegst, dann hat deine Hierarchie keinen virtuellen Destruktor und jede Zerstörung eines dynamischen erzeugten Objekts einer abgeleiteten Klasse über einen Basisklassenzeiger führt zu undefiniertem Verhalten.



  • ah 🙂



  • Ach ja: Danke für die großartige Hilfe. Ohne dich säße ich wahrscheinlich noch da und hätte keine Ahnung, wo der Fehler liegt (lag).


Anmelden zum Antworten