type_trait zum überprüfen ob typ komplett definiert ist



  • Hi,

    ich brauche ein type_trait zum überprüfen ob ein Typ komplett definiert ist oder nicht. Hat jemand ein code snippet dafür?

    class A;
    
    static_assert(is_complete<A>::value, "type is not defined");
    


  • Wenns nur um das static_assert geht, dann gibt's da ne mehr oder weniger einfache Möglichkeit:

    // Aus boost::checked_delete():
    
        // intentionally complex - simplification causes regressions
        typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
        (void) sizeof(type_must_be_complete);
    

    Ich vermute allerdings ein static_assert(sizeof(T) > 0) müsste auch reichen.

    Per Type-Trait kann es auf jeden Fall nicht gehen. Denn der Wert von type_traits<T>::xxx muss ja identisch sein, egal an welcher Stelle im Programm man den Wert "anguckt".
    Andernfalls kämen da sicherliche viele Würmer aus der Dose gekrochen...


  • Mod

    sizeof darf auf unvollstaendige Typen gar nicht angewandt werden. Daher schlage ich

    template <class, class=void> struct is_complete : std::false_type {};
    template <class T>
    struct is_complete<T, decltype(void(sizeof(T)))> : std::true_type {};
    

    vor.



  • Und was soll das bringen?

    BTW: dass sizeof auf unvollständige Typen nicht angewendet werden darf ist ja auch das Grund dass und der Mechanismus über den der aus der Boost zitierte Code funktioniert.



  • @Arcoth
    Funktioniert, klasse!
    https://ideone.com/Z2XpR7

    Kannst du bitte kurz erklären wie das funktioniert?

    @hustbaer
    ich wollte halt gern ein type traits haben, der dann nicht zwangsläufig in ein compile timer error mündet.

    Komisches kommentar aus dem boost code:

    // intentionally complex - simplification causes regressions



  • @Arcoth: Ahm. Wie soll das funktionieren wenn in einer TU der betreffende Typ erst an Stelle x passend instantiiert wird du aber is_complete vorher und nachher auswertest?



  • Nash26 schrieb:

    @hustbaer
    ich wollte halt gern ein type traits haben, der dann nicht zwangsläufig in ein compile timer error mündet.

    Komisches kommentar aus dem boost code:

    // intentionally complex - simplification causes regressions

    Das heisst dass es absichtlich so kompliziert gemacht wurde, weil einfachere Varianten mit manchen Compilern nicht funktionieren.
    Regression = Rückschritt = wenn man nen Fehler hat bei etwas was schonmal funktioniert hat.

    D.h. da stand vermutlich die komplizierte Version und alles war gut.
    Und dann hat sie jmd. vereinfacht, und alles war nicht mehr gut.
    Und dann wurde wieder die komplizierte Version reingeschrieben, mit diesem Kommentar dazu, damit sie der nächste Herr Schlau nicht wieder vereinfacht.

    Wobei das auch ne reichlich alte Boost Version ist wo ich das raus habe. Wie die aktuellen aussieht weiss ich nicht.

    ----

    Ansonsten... Ich hab die Variante von Arcoth 'nu auch endlich verstanden. Hab' aber keine Ahnung ob das laut Standard funktionieren muss, da ich net weiss ob sizeof(NotFullyDefined) Fehler laut Standard für SFINAE gelten müssen.



  • @swordfish

    Da ist in der tat etwas nicht ganz koscher:
    https://ideone.com/I4EK8V

    Nachdem ich den Typ definiert habe, bekomme ich trotzdem eine Fehlermeldung..
    Ich hätte jetzt erwartet das ich keinen Fehler mehr bekomme.

    @Arcoth
    Was sagst du dazu?


  • Mod

    BTW: dass sizeof auf unvollständige Typen nicht angewendet werden darf ist ja auch das Grund dass und der Mechanismus über den der aus der Boost zitierte Code funktioniert.

    ... ich weiß. Habe ich etwas widersprüchliches behauptet?

    Denn der Wert von type_traits<T>::xxx muss ja identisch sein, egal an welcher Stelle im Programm man den Wert "anguckt".

    Ich glaube du meinst

    [temp.point]/8 schrieb:

    A specialization for any template may have points of instantiation in multiple translation units. If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required.

    Das hält aber das Trait an einem bestimmten Punkt nicht auf. Verwendet man jedoch dieselbe Spezialisierung an mehreren Orten wird es ggf. sinnlos und kann auch das Programm ill-formed NDR machen, genauso wie das Verwenden innerhalb eines Headers der in mehreren ÜEs inkonsistent eingebunden wird.

    Was sagst du dazu?

    Ich nahm natürlich an dass das Trait für einen Typen jeweils nur für diese eine assertion verwendet wird. Denn ein Template wird pro Übersetzungseinheit nur einmal instantiiert. Du kannst

    template <class, int, class=void> struct is_complete : std::false_type {};
    template <class T, int i>
    struct is_complete<T, i, decltype(void(sizeof(T)))> : std::true_type {};
    
    #define IS_COMPLETE(...) is_complete<__VA_ARGS__, __COUNTER__>::value
    

    Probieren (Demo), aber Vorsicht ist geboten.



  • Arcoth schrieb:

    Denn der Wert von type_traits<T>::xxx muss ja identisch sein, egal an welcher Stelle im Programm man den Wert "anguckt".

    Ich glaube du meinst

    [temp.point]/8 schrieb:

    A specialization for any template may have points of instantiation in multiple translation units. If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required.

    Ja, das meine ich. Ich merk mir die jeweiligen Stellen nie, wie findest du das immer so schnell?

    Arcoth schrieb:

    Das hält aber das Trait an einem bestimmten Punkt nicht auf. Verwendet man jedoch dieselbe Spezialisierung an mehreren Orten wird es ggf. sinnlos und kann auch das Programm ill-formed NDR machen, genauso wie das Verwenden innerhalb eines Headers der in mehreren ÜEs inkonsistent eingebunden wird.

    Klar kann man es 1x wo verwenden. Aber wenn man es nur 1x irgendwo verwenden darf, und sonst nirgends, weil man sonst "ill-formed" bekommt ... pfuh. Sowas verwende ich nicht.
    Da verlasse ich mich lieber auf non-Standard Verhalten wie dass der Compiler bei sizeof(NotFullyDefined) einfach nen Fehler wirft, obwohl er es nicht müsste (Oder muss er es? So genau kenne ich den Standard nicht - keine Ahnung ob das ein "diagnostic required" Fall ist).

    Und da mir kein anderer Grund einfällt wissen zu wollen ob irgend ein Typ "fully defined" ist, als im Falle dass nicht einen Fehler zu erzeugen...


  • Mod

    Klar kann man es 1x wo verwenden. Aber wenn man es nur 1x irgendwo verwenden darf, und sonst nirgends, weil man sonst "ill-formed" bekommt ... pfuh. Sowas verwende ich nicht.

    Verständlich, ich würde es ggf. auch nicht tun.

    Oder muss er es?

    Er muss es

    [expr.sizeof]/1 schrieb:

    The sizeof operator shall not be applied to an expression that has [..] incomplete type

    [intro.compliance]/1 schrieb:

    The set of diagnosable rules consists of all syntactic and semantic rules in this International Standard except for those rules containing an explicit notation that “no diagnostic is required” or which are described as resulting in “undefined behavior.”

    Obiges trifft also auch auf die sizeof constraints zu. Und damit muss die Implementierung mindestens eine diagnostic message ausgeben:

    [intro.compliance]/(2.2) schrieb:

    If a program contains a violation of any diagnosable rule [..] a conforming implementation shall issue at least one diagnostic message.

    Das kann theoretisch auch eine Warnung sein, aber für unvollständige Typen kann sizeof keinen sinnvollen Wert annehmen. Zumindest kenne ich keine Implementierung die das mit einer Warnung kompilieren würde.

    I.e. die sicherste Lösung wäre einfach ein expression statement mit einem Kommentar zu schreiben:

    sizeof(A); /* If you get an error message here, A is not complete.
                  Please provide a definition of A before this line. */
    

    wie findest du das immer so schnell?

    Entsprechende Suchbegriffe per Strg+F. Hier habe ich einfach bei Kapitel 14 angefangen nach ill-formed zu suchen, hatte das Zitat nach einigen Minuten.



  • Arcoth schrieb:

    I.e. die sicherste Lösung wäre einfach ein expression statement mit einem Kommentar zu schreiben:

    sizeof(A); /* If you get an error message here, A is not complete.
                  Please provide a definition of A before this line. */
    

    Ich erinnere nochmal an das Kommentar im Boost Code
    // intentionally complex - simplification causes regressions
    Ich glaub nicht dass das ein Scherz ist 😉
    => Einfach nur sizeof(A); wird ziemlich sicher nicht für alle Compiler reichen.


Log in to reply