reinterpret_cast und wieder zurück immer möglich?



  • Hi,

    um Daten weiterzuleiten, möchte ich diese generisch abspeichern.
    Also nur Zeiger auf die Daten und dann wieder zurück konvertieren.
    Z.b. möchte ich einen Zeiger auf Objekte in ein void* abspeichern.
    Also mit reinterpret_cast nach void* und dann wieder zurück zum Original Typ.

    In etwa so:

    class dummy_class
    {
    };
    typedef void (*void_func_ptr)();
    typedef void (dummy_class::*void_mem_func_ptr)();
    typedef void* (dummy_class::*void_mem_obj_ptr);
    
    union data_container
    {
        void*               obj_ptr;      // Stores a pointer to an object
        void_mem_obj_ptr    mem_obj_ptr;  // Stores a pointer to a member object
        void_func_ptr       func_ptr;     // Stores a pointer to a function
        void_mem_func_ptr   mem_func_ptr; // Stores a pointer to a member function
    };
    

    Der Standard sagt dazu auch etwas in §5.2.10.10
    So wie ich das jetzt verstanden habe sollte das auch alles funktionieren.
    Nur beim konvertieren eines "pointer to a data member" bin ich unschlüssig, das verstehe ich nicht ganz:

    converting a prvalue of type “pointer to data member of X of type T1” to the type “pointer to data
    member of Y of type T2” (where the alignment requirements of T2 are no stricter than those of T1)
    and back to its original type yields the original pointer to member value.

    Kann mir jemand genau erklären was mit alignment requirements gemeint ist?



  • Jeder Typ T hat einen "alignment requirement" Wert.
    Das ist einfach ne Zahl. Und durch diese Zahl muss die Adresse an der sich ein T -Objekt befinden darf ohne Rest teilbar sein.

    Auf einigen Systemen dürfen 4-Byte Integer z.B. nur an durch 4 teilbaren Adressen liegen. Weil die CPU sie nur von solchen Adressen lesen bzw. auf solche Adressen schreiben kann. Auf so einen System wäre der "alignment requirement" Wert eines 4-Byte Integers also 4.

    Siehe auch
    http://en.cppreference.com/w/cpp/language/object#Alignment

    ps:

    typedef void* (dummy_class::*void_mem_obj_ptr);
    

    Ich behaupte mal da ist ein * zuviel.


  • Mod

    hustbaer schrieb:

    ps:

    typedef void* (dummy_class::*void_mem_obj_ptr);
    

    Ich behaupte mal da ist ein * zuviel.

    Ein Member kann nicht den Typ void haben.
    Allerdings ist void* tatsächlich nicht geeignet, eben wegen der alignment-Voraussetzungen. char, mithin

    char dummy_class::* mem_obj_ptr;
    

    wäre angebracht.

    Im Übrigen sollten Konvertierungen von und nach void* üblicherweise nicht mittels reinterpret_cast durchgeführt werden.



  • @camper erklär mir bitte warum ein char verwendet werden muss, das verstehe ich nicht:

    typedef char (dummy_class::*void_mem_obj_ptr);
    

    Ich kann kein static_cast verwenden, im standard sind diese Operationen nur für reinterpret_cast definiert. Siehe §5.2.10

    Kannst du selbst mit g++ ausprobieren, ich bekomme auch einen compile fehler.



  • camper schrieb:

    hustbaer schrieb:

    ps:

    typedef void* (dummy_class::*void_mem_obj_ptr);
    

    Ich behaupte mal da ist ein * zuviel.

    Ein Member kann nicht den Typ void haben.
    Allerdings ist void* tatsächlich nicht geeignet, eben wegen der alignment-Voraussetzungen.

    Hm. Ich hab' einfach mal angenommen dass für Member-Pointer was void angeht die selben Regeln gelten wie für normale Zeiger.
    Ein Objekt kann ja auch nicht den Typ void haben, trotzdem gibt es void-Pointer. Und diese sind auf geeignet auf alles zu zeigen, egal was für ein Alignment es hat.
    Wenn für Member-Pointer andere Regeln gelten würde ich das zumindest als verwirrend bezeichnen 🙂


  • Mod

    Nash26 schrieb:

    @camper erklär mir bitte warum ein char verwendet werden muss, das verstehe ich nicht:

    typedef char (dummy_class::*void_mem_obj_ptr);
    

    Weil char die geringsten alignment requirements aller Typen hat.

    Nash26 schrieb:

    Ich kann kein static_cast verwenden, im standard sind diese Operationen nur für reinterpret_cast definiert. Siehe §5.2.10

    Kannst du selbst mit g++ ausprobieren, ich bekomme auch einen compile fehler.

    Ich habe meiner vorherigen Antwort diesbezgl. nichts hinzuzufügen. Evtl. solltest du nochmal lesen, was du selbst zuvor geschrieben hast.


  • Mod

    hustbaer schrieb:

    camper schrieb:

    hustbaer schrieb:

    ps:

    typedef void* (dummy_class::*void_mem_obj_ptr);
    

    Ich behaupte mal da ist ein * zuviel.

    Ein Member kann nicht den Typ void haben.
    Allerdings ist void* tatsächlich nicht geeignet, eben wegen der alignment-Voraussetzungen.

    Hm. Ich hab' einfach mal angenommen dass für Member-Pointer was void angeht die selben Regeln gelten wie für normale Zeiger.
    Ein Objekt kann ja auch nicht den Typ void haben, trotzdem gibt es void-Pointer. Und diese sind auf geeignet auf alles zu zeigen, egal was für ein Alignment es hat.
    Wenn für Member-Pointer andere Regeln gelten würde ich das zumindest als verwirrend bezeichnen 🙂

    Zeiger auf Member sind keine Zeiger.
    Übrigens ist der Standard hier auch sprachlich konsistent
    void* ist eine 'object pointer' aber kein 'pointer to object'.
    Auch zeigt void* auf nichts, schließlich kann void nicht dereferenziert werden.
    Kurz: void ist sowieso speziell.


  • Mod

    Und warum kompiliert dann

    struct A {
        using type = A(A::*);
    };
    

    😃



  • camper schrieb:

    Ich habe meiner vorherigen Antwort diesbezgl. nichts hinzuzufügen. Evtl. solltest du nochmal lesen, was du selbst zuvor geschrieben hast.

    Meinen Hinweis mit dem static_cast hast du gelesen? Den kann man dafür nicht verwenden.

    Ok, aber ich verstehe Deine Antwort trotz mehrmaligen lesen immer noch nicht.
    Warum char und nicht void*.

    Hat es jemand anderes vielleicht verstanden und kann es mir erklären?


  • Mod

    Nash26 schrieb:

    Z.b. möchte ich einen Zeiger auf Objekte in ein void* abspeichern.
    Also mit reinterpret_cast nach void* und dann wieder zurück zum Original Typ.

    camper schrieb:

    Im Übrigen sollten Konvertierungen von und nach void* üblicherweise nicht mittels reinterpret_cast durchgeführt werden.

    Nash26 schrieb:

    Ich kann kein static_cast verwenden, im standard sind diese Operationen nur für reinterpret_cast definiert. Siehe §5.2.10

    Beipiele. bringen. Ich bin extrem emfindlich, wenn zu Widersprüchen keine nachvollziehbare Begründung geliefert wird.
    Natürlich wird reinterpret_cast erforderlich, wenn es um Zeiger auf Member (oder Funktionszeiger) geht. Davon ist in diesen Sätzen aber keine Rede.

    Nash26 schrieb:

    @camper erklär mir bitte warum ein char verwendet werden muss, das verstehe ich nicht:

    typedef char (dummy_class::*void_mem_obj_ptr);
    

    Ich zitiere deine Quelle:

    converting a prvalue of type “pointer to data member of X of type T1” to the type “pointer to data
    member of Y of type T2” (where the alignment requirements of T2 are no stricter than those of T1)
    and back to its original type yields the original pointer to member value.

    was liegt näher als für T2 einen Typ mit den geringsten Alignmentvoraussetzungen zu verwenden?



  • @camper
    Dass unter diesen Voraussetzungen char gehen muss ist (mir) klar.
    Nur nicht warum void nicht gehen soll.

    Dass ein "Zeiger auf Member" nicht das selbe ist wie ein Zeiger, ist auch klar.
    Der Unterschied ist aber mMn. minimal.
    Beide kann man als Offset verstehen. Bei einem normalen Zeiger ist die Basisadresse dabei halt konstant, bei einem "Zeiger auf Member" ... nicht.

    Bei beiden sind Alignment-Requirements wichtig. Bei beiden ist es so dass void eigentlich keinen Sinn macht (weil es weder void Objekte noch void Member gibt), aber schön als Joker verwendet werden kann.
    Für "normale" Zeiger erlaubt das Standard die Verwendung von void explizit. Ob der Standard das für "Zeiger auf Member" erlaubt, weiss ich nicht.


  • Mod

    8.3.3/3
    A pointer to member shall not point to a static member of a class (9.4), a member with reference type, or “cv void.”



  • Ah, OK, danke.


Anmelden zum Antworten