Funktionszeiger serialisieren



  • Mir ist bekannt, wie man zur Laufzeit einen Funktionszeiger erstellt.
    Jetzt würde ich diesen Zeiger gerne serialisieren (= speichern). Da ich annehme, dass das Speichersegment von Fall zu Fall ein anderes sein wird, denke ich, dass man statt einer Speicheradresse den Namen der Funktion aabspeichern müsste, auf die dann zur Laufzeit verwiesen werden soll.

    Beispiel: Ich habe folgenden Funktionszeiger:

    void (*pFunc)();
    

    der verwiest auf Funktionen vom Typ:

    void SomeFunc(){};
    

    Die Zuweisung sieht zur Laufzeit so aus:

    pFunc = SomeFunc;
    

    und der Aufruf:

    (*pFunc)();
    

    Das von SomeFunc belegte Speichersgemnet wird sich freilich ändern, so dass man nicht ein Speichersegment, sondern sinnvollerweise den Namen der Datei, "SomeFunc" speichern siollte.
    Aber wie lässt sich dieses char* Array zuück casten, so dass ich nicht den Namen der Datei, sondern das Speichersegment bekomme?



  • Schau Dir mal std::map an. Die Strings lassen sich allerdings nicht automatisch generieren. Du musst schon selbst jeder Funktion eine entsprechende Zuordnung zu einem String verpassen.



  • Leicht OT: Gibt es eigentlich in C++ eine schöne Abbildung von 1:1-Beziehungen ? In diesem Fall sollte ja nicht nur der key sondern auch der value eindeutig sein ...
    Kann man natürlich selbst bauen - aber vorgegeben gibt's das nicht, oder ?

    Gruß,

    Simon2.

    P.S.: Außerdem fände ich eine map, deren operator[] auch eine "not_found-Exception" werfen kann, wünschenswert. 😃



  • Meiner meinung nach, sollten sihc die Adressen der Funktionen nicht ändern (auf jeden Fall nicht in Exe Dateien, bei dlls bin ich mir nicht sicher.
    Es sollte also ohne weiteres möglich sein, diese Abzuspeichern.
    Es kann natürlich sein, dass nach einem Rebuild nach minimalen Änderungen am Code, die abgespeicherte Adresse nicht mehr stimmt.

    Code:
    pFunc = SomeFunc;
    

    Das speichern des Funktionspointers sieht aber so aus:

    Code:
    pFunc = &SomeFunc;
    

    Erlär doch mal wozu du das brauchst?
    Unter c++ gibt es viel bessere Konzepte: virutelle funktionen, Funktoren

    Ich würd mal vermuten, das da ein schlechtes Design gewählt wurde.



  • vlad_tepesch schrieb:

    Meiner meinung nach, sollten sihc die Adressen der Funktionen nicht ändern (auf jeden Fall nicht in Exe Dateien, bei dlls bin ich mir nicht sicher....

    😕
    Aber das Programm wird doch bei einem neuen Start höchstwahrscheinlich nicht an derselben Speicherstelle liegen, oder ?
    "member function pointer" tragen bestimmt nur "relative Adressen", aber normale Funktionszeiger haben doch "absolute Adressen" ....

    vlad_tepesch schrieb:

    ...
    Das speichern des Funktionspointers sieht aber so aus:

    Code:
    pFunc = &SomeFunc;
    

    ...

    Soweit ich weiß, ist aber Ersteres auch gültig.

    Gruß,

    Simon2.



  • @Taychon
    das war sehr hilfreich.



  • @Taychon
    das war sehr hilfreich.

    @Simon @vlad
    - ja, es funktioniert auch ohne den Adressooperator. - Hintergrund der Serialisierung: es ist tatsächlich eine dll. Tatsächlich sollte ich versuchen, ob auch da eine absolute Adresse möglich ist - hab's in der Annahmme, dass sich da was ändert, noch gar nicht ausprobiert.

    Wozu ich's brauche? In einer Datei werden Regelsätze gespeichert - im Sinne einer Grammatik -, die mit Funktionen verknüpft werden sollen.
    Schon aus Speicherplatzgründen wäre es mir natürlich sehr viel lieber, wenn man eine einzige long Variable statt eines langen char Arrays nutzen könnte.

    Was wären die Alternativen?



  • Schon aus Speicherplatzgründen wäre es mir natürlich sehr viel lieber, wenn man eine einzige long Variable statt eines langen char Arrays nutzen könnte.

    Was wären die Alternativen?

    Wenn du das alles per String identifizieren willst kaum etwas anderes, als dir das irgendwie zu indexieren und dann kannst du auch std::vector nutzen, anstatt die schwere map. (Allerdings kann ich mir kaum vorstellen, dass es auf das bisschen an Speicher drauf ankommt.. 🙄 )



  • Percy2000 schrieb:

    @Taychon
    das war sehr hilfreich.

    @Simon @vlad
    - ja, es funktioniert auch ohne den Adressooperator. - Hintergrund der Serialisierung: es ist tatsächlich eine dll. Tatsächlich sollte ich versuchen, ob auch da eine absolute Adresse möglich ist - hab's in der Annahmme, dass sich da was ändert, noch gar nicht ausprobiert.

    Wozu ich's brauche? In einer Datei werden Regelsätze gespeichert - im Sinne einer Grammatik -, die mit Funktionen verknüpft werden sollen.
    Schon aus Speicherplatzgründen wäre es mir natürlich sehr viel lieber, wenn man eine einzige long Variable statt eines langen char Arrays nutzen könnte.

    Was wären die Alternativen?

    Also, ich würde es immer über eine Character basierste Zuordungstabelle machen. Schon alleine aufgrund der Portabilität.



  • vlad_tepesch schrieb:

    Unter c++ gibt es viel bessere Konzepte: virutelle funktionen, Funktoren

    Inwiefern diese Konzepte einen Ersatz für die Sprachunterstützung von Serialisierung darstellen sollen, erschließt sich mir nicht.

    Percy2000 schrieb:

    Tatsächlich sollte ich versuchen, ob auch da eine absolute Adresse möglich ist - hab's in der Annahmme, dass sich da was ändert, noch gar nicht ausprobiert.

    Wenn du mit den Konsequenzen (nach jedem erneuten Link können sich die Adressen ändern, bei DLLs auch von Programmstart zu Programmstart) leben kannst, anders gesagt: wenn die Datei ohnehin nur für die Laufzeit des Programmes Gültigkeit behalten soll, dann geht das. Ansonsten ist so etwas dringend zu unterlassen.

    Percy2000 schrieb:

    Was wären die Alternativen?

    Wenn es kein Standard-C++ sein muß:

    #include <vcl.h> // C++Builder
    #pragma hdrstop
    
    class TAssociatedFunctions : public TObject
    {
    __published:
        static void foo (void) { ShowMessage ("foo"); }
        static void bar (void) { ShowMessage ("bar"); }
    };
    
    int main (void)
    {
        void (*func) (void);
        void (*func2) (void);
    
        func = &TAssociatedFunctions::foo;
        String funcname = TAssociatedFunctions::MethodName (func);
        func2 = reinterpret_cast <void (*) (void)> (
            TAssociatedFunctions::MethodAddress (funcname));
        func2 (); // zeigt "foo" an
    }
    

    Falls doch, dann bleibt dir wenig anderes als eine Zuordnungstabelle wie std::map <std::string, void (*) (void)>, die zum Programmstart gefüllt wird.

    Percy2000 schrieb:

    Schon aus Speicherplatzgründen wäre es mir natürlich sehr viel lieber, wenn man eine einzige long Variable statt eines langen char Arrays nutzen könnte.

    Dann könntest du ja z.B. aus den Funktionsnamen einen Hash generieren.

    Für die C++Builder-Lösung könnte das z.B. so aussehen:

    #include <vcl.h>
    #include <map>
    #include <cstddef>
    #pragma hdrstop
    
    unsigned simpleHashFunc (const char* str)
    {
        unsigned retval = 0;
    
        if (str)
            while (*str)
                retval = ((retval << 3) | (retval & 0x80000000) >> 29) ^ *(str++);
        return retval;
    }
    
    template <class T, typename HashVal, HashVal (*HashFunc) (const char*)>
        class MethodHashSerializer
    {
    private:
        std::map <HashVal, String> rainbow_table;
    
    #pragma pack(push, 1)
        struct TMethodTableEntry
        {
            WORD        RecordLen;
            void*       Addr;
            ShortString Name;
    
        };
    #pragma pack(pop)
    
    public:
        MethodHashSerializer (void)
        {
            TMetaClass* mc = __classid (T);
            char* mtp = *reinterpret_cast <char**> (
                reinterpret_cast <char*> (mc) + vmtMethodTable);
            TMethodTableEntry* mte;
            UTF8String methodName;
    
            unsigned methodCount = *reinterpret_cast <WORD*> (mtp);
            mtp += sizeof (WORD);
    
            while (methodCount-- > 0)
            {
                mte = reinterpret_cast <TMethodTableEntry*> (mtp);
                mtp += mte->RecordLen;
    
                methodName = UTF8String (mte->Name);
                rainbow_table[HashFunc (methodName.c_str ())] = methodName.c_str ();
            }
        }
    
        static HashVal serialize (void* addr)
        {
            UTF8String name = __classid (T)->MethodName (addr);
            return simpleHashFunc (name.c_str ());
        }
        void* unserialize (HashVal val)
        {
            std::map <HashVal, String>::const_iterator i
                = rainbow_table.find (val);
            if (i == rainbow_table.end ())
                return 0;
            return __classid (T)->MethodAddress (i->second);
        }
    };
    
    class TAssociatedFunctions : public TObject
    {
    __published:
        static void foo (void) { ShowMessage ("foo"); }
        static void bar (void) { ShowMessage ("bar"); }
    };
    
    int main (void)
    {
        void (*func) (void);
        void (*func2) (void);
    
        MethodHashSerializer <TAssociatedFunctions, unsigned, simpleHashFunc> hs;
    
        func = &TAssociatedFunctions::foo;
        unsigned funchash = hs.serialize (func);
        func2 = reinterpret_cast <void (*) (void)> (hs.unserialize (funchash));
        func2 (); // zeigt "foo" an
    }
    

Anmelden zum Antworten