[C/C++] const auf integrale typen in einer Funktion sinnvoll?



  • ;fricky schrieb:

    aussagekräftiger als 'const' finde ich z.b. die info zur datenrichtung bei den prototypen in einigen windows-headern, die schreiben __in, __out und __inout vor ihre pointer (wahrscheinlich in anlehnung an VHDL, wo in,out,inout bzw 'buffer' als port-modes auch vom compiler gecheckt werden).

    Da ich zufällig in VHDL "programmieren" kann, würde ich sagen, es hat nichts mit VHDL zu tun. VHDL ist "zu anders" und man kann es weder mit C noch C++, noch mit Assembler vergleichen, denke ich, abgesehen von der Syntax vielleicht.
    Interessant wäre, wie sind diese __in, __out und __inout definiert? __in bestimmt als const, oder? 🙂



  • abc.w schrieb:

    Interessant wäre, wie sind diese __in, __out und __inout definiert? __in bestimmt als const, oder?

    nö, ich glaube das sind einfach nur leere #defines, damit der benutzer am funktionskopf sehen kann, in welcher richtung die pointer arbeiten. 'const' könnte man ja für 'in' nehmen, 'ohne const' für 'inout', aber für eine reine 'out'-datenrichtung sieht C nix vor (sowas wie ein 'inverses const', das wäre ein pointer, über den man schreibzugriffe machen, aber nicht lesen darf).
    🙂



  • abc.w schrieb:

    Da ich zufällig in VHDL "programmieren" kann, würde ich sagen, es hat nichts mit VHDL zu tun. VHDL ist "zu anders" und man kann es weder mit C noch C++, noch mit Assembler vergleichen, denke ich, abgesehen von der Syntax vielleicht.

    nachtrag: die syntax ist an ADA angeleht, aber dieses konzept der port-modi könnte man auch in 'normalen' programmiersprachen verwenden.
    🙂



  • ;fricky schrieb:

    nö, ich glaube das sind einfach nur leere #defines, damit der benutzer am funktionskopf sehen kann, in welcher richtung die pointer arbeiten...

    Oder hat Microsoft vielleicht eine "Spezialversion" ihres Compilers, der diese Schlüsselwörter kennt und vielleicht die eine oder andere Warnung ausgibt, wenn z.B. etwas als __in definiert wurde und in der Funktion doch geändert wird, oder etwas als __out nichts zugewiesen bekommt, sondern fälschlicherweise gelesen wird... Wäre denkbar? 🙂



  • abc.w schrieb:

    ;fricky schrieb:

    nö, ich glaube das sind einfach nur leere #defines, damit der benutzer am funktionskopf sehen kann, in welcher richtung die pointer arbeiten...

    Oder hat Microsoft vielleicht eine "Spezialversion" ihres Compilers, der diese Schlüsselwörter kennt und vielleicht die eine oder andere Warnung ausgibt, wenn z.B. etwas als __in definiert wurde und in der Funktion doch geändert wird, oder etwas als __out nichts zugewiesen bekommt, sondern fälschlicherweise gelesen wird... Wäre denkbar?

    zuzutrauen ist es ihnen jedenfalls. kannst ja mal im winapi-forum fragen, da tummeln sich die m$-compiler freaks.
    🙂



  • Einfach mit MSVC++ mit /analyze compilieren.

    http://msdn.microsoft.com/en-us/library/ms173498.aspx

    Bzw. früher hiess das Teil PREfast:
    http://msdn.microsoft.com/en-us/library/ms933794.aspx



  • ;fricky schrieb:

    hustbaer schrieb:

    ok, fricky, sorry. mein fehler.
    kreativen const-schummel code zu schreiben ist natürlich produktiver als sauber zu programmieren. wie konnte ich nur was anderes glauben.

    wieso 'schummel'?

    const wegcasten, und dann etwas modifizieren, ist schummeln. du behauptest ja hier, dass man sich nicht auf const verlassen kann. das impliziert, dass es OK ist, code zu schreiben, der genau das macht: const wegcasten, und dann modifizieren. und das halte ich nunmal für völlig daneben.

    schnittstellen müssen sowieso gut dokumentiert sein, wenn andere damit arbeiten sollen. 'const' hilft dabei nur bedingt.

    was heisst bedingt? bedingt wie "hilft viel, aber ist nicht 100% ausreichend"? oder bedingt wie "ist so-gut-wie egal"? sag mal konkret was du meinst.

    aussagekräftiger als 'const' finde ich z.b. die info zur datenrichtung bei den prototypen in einigen windows-headern, die schreiben __in, __out und __inout vor ihre pointer (wahrscheinlich in anlehnung an VHDL, wo in,out,inout bzw 'buffer' als port-modes auch vom compiler gecheckt werden).

    __in macht eigentlich nur sinn, wenn ein zeiger nicht const ist, der const sein sollte.
    einzig die unterscheidung zwischen __inout und __out ist etwas, was sich über const nicht abbilden lässt. im idealfall impliziert der methodenname, der parametername oder einfach die semantik der schnittstelle was gemeint ist.

    bei einer funktion "apply" einer klasse "inplace_blur_filter" brauche ich nicht dazuschreiben dass es "in/out" ist, da es logisch ist.

    sollte es nicht logisch sein, dann gehört es dokumentiert. habe nie was anderes behauptet.

    ich behaupte nur, dass const einen nicht unwesentlichen teil dessen was dokumentiert gehört abdeckt, und dass der compiler die checks übernimmt, ob man sich auch an das hält, was man verspricht. genau das ist ja der grosse vorteil dabei.

    eine doku die irgendwo steht, kann der compiler nicht checken. und dass doku sehr schnell veraltet, ist etwas was ich nicht nur glaube, sondern aus der praxis 100% bestätigen kann.



  • hustbaer schrieb:

    ;fricky schrieb:

    hustbaer schrieb:

    ok, fricky, sorry. mein fehler.
    kreativen const-schummel code zu schreiben ist natürlich produktiver als sauber zu programmieren. wie konnte ich nur was anderes glauben.

    wieso 'schummel'?

    const wegcasten, und dann etwas modifizieren, ist schummeln.

    nö, wenn du z.b. 'ne funktion aufrufen willst, bei der das 'const' fehlt, die aber definitiv keine schreibzugriffe über diesen pointer macht, musstes wegcasten. das ist nichts schlimmes und funktioniert auch.

    hustbaer schrieb:

    schnittstellen müssen sowieso gut dokumentiert sein, wenn andere damit arbeiten sollen. 'const' hilft dabei nur bedingt.

    was heisst bedingt? bedingt wie "hilft viel, aber ist nicht 100% ausreichend"? oder bedingt wie "ist so-gut-wie egal"? sag mal konkret was du meinst.

    das kommt drauf an. wenn 'const' logisch passt, unterstützt es die lesbarkeit. wenn aber z.b. da steht: 'void funktion (const void *p)', dann ist klar, dass das interface irgendwie mist ist, weil mit ziemlicher sicherheit im innern gecastet wird.

    btw, nochwas zu deinen casting-verbotsregeln 'wer const wegcastet wird gefeuert', eine kleine quizfrage: wie bekommt man 'const' weg, ohne irgendeinen cast zu benutzen.?
    🙂



  • Gar nicht(!), sondern indem man 100-prozentige 'const-correctness' verwendet (halbe oder dreiviertel const-correctness ist Frickelei -)



  • Th69 schrieb:

    Gar nicht(!)

    doch, 'entconsten' geht ohne cast. wer errät den trick (ist garnicht so schwer)?
    🙂



  • ;fricky schrieb:

    nö, wenn du z.b. 'ne funktion aufrufen willst, bei der das 'const' fehlt, die aber definitiv keine schreibzugriffe über diesen pointer macht, musstes wegcasten. das ist nichts schlimmes und funktioniert auch.

    Doch, das ist ein schlimmer Designfehler, der bei durchdachter Programmierung nicht auftritt. Dass gewisse Bibliotheken schlecht programmiert sind, rechtfertigt const_cast nicht per se.

    ;fricky schrieb:

    das kommt drauf an. wenn 'const' logisch passt, unterstützt es die lesbarkeit. wenn aber z.b. da steht: 'void funktion (const void *p)', dann ist klar, dass das interface irgendwie mist ist, weil mit ziemlicher sicherheit im innern gecastet wird.

    Wieso, es kann ja auch sein, dass nur lesend zugegriffen wird, auch wenn p in einen Typzeiger gecastet werden muss. Ich finde Funktionen, die bei ihrer Schnittstelle heucheln, als würden sie Argumente nicht verändern, völlig daneben.

    ;fricky schrieb:

    doch, 'entconsten' geht ohne cast. wer errät den trick (ist garnicht so schwer)?

    Das wäre eine Möglichkeit:

    union conster
    {
    	const int* m;
    	int* n;
    };
    
    int main()
    {
    	const int a = rand() >= 0 ? 5 : 2; // damit kein konstanter Ausdruck
    
    	conster c;
    	c.m = &a;
    	(*c.n) = 7;
    }
    


  • Nexus schrieb:

    ;fricky schrieb:

    nö, wenn du z.b. 'ne funktion aufrufen willst, bei der das 'const' fehlt, die aber definitiv keine schreibzugriffe über diesen pointer macht, musstes wegcasten. das ist nichts schlimmes und funktioniert auch.

    Doch, das ist ein schlimmer Designfehler, der bei durchdachter Programmierung nicht auftritt. Dass gewisse Bibliotheken schlecht programmiert sind, rechtfertigt const_cast nicht per se.

    ich dachte eigentlich, der const_cast wäre genau für sowas gut, nämlich ein nachträglich ge-constetes objekt mal wieder zu ent-consten. doof ist natürlich, dass man in einer funktion selbst nicht erkennen kann, ob es sich bei dem vom aufrufer erhaltenen pointer nur um eine 'verkleidung', oder um ein tatsächlich konstantes objekt handelt, denn im zweiten fall wäre ein const-wegcast ja undefiniert.

    Nexus schrieb:

    ;fricky schrieb:

    doch, 'entconsten' geht ohne cast. wer errät den trick (ist garnicht so schwer)?

    Das wäre eine Möglichkeit:

    union conster
    {
    	const int* m;
    	int* n;
    };
    ...
    

    genau das meinte ich. weitere möglichkeiten gibts wohl nicht, oder kennt noch jemand eine?
    jedenfalls sollte husti auch unions in seine verbotsliste aufnehmen.
    🙂



  • ;fricky schrieb:

    ich dachte eigentlich, der const_cast wäre genau für sowas gut, nämlich ein nachträglich ge-constetes objekt mal wieder zu ent-consten.

    Der Const-Cast ist mehr ein Workaround für bereits schlechtes Design als ein wirklich oft sinnvoll eingesetztes Sprachmittel. Was hat Const-Correctness für einen Sinn, wenn man sich nicht daran hält? Wenn man ein Objekt als const deklariert, geht man davon aus, dass es nicht geändert wird. Daran kann man sich gefälligst halten...

    ;fricky schrieb:

    genau das meinte ich. weitere möglichkeiten gibts wohl nicht, oder kennt noch jemand eine?

    Gibt noch ein paar Möglichkeiten, besonders mit Strukturen (obwohl diese sehr ähnlich sind). Du hast zu wenig Fantasie! 😉
    Dass da recht schnell undefiniertes Verhalten dazu kommt, sollte klar sein.

    // 1)
    int n = 1;
    const int m = 5*n;
    *(&n + (&m - &n)) = 42;
    
    // 2)
    const int o = 5*n;
    struct
    {
    	int* n;
    	const int* m;
    } z = {&n, &o};
    **(&z.n + 1) = 42;
    
    // 3)
    struct
    {
    	int n;
    	const int m;
    } a = {1, 5};
    *(&a.n + 1) = 42;
    
    // 4)
    struct tmp
    {
    	const int p;
    };
    tmp b = {5};
    tmp h = {42};
    new (&b) tmp(h);
    
    // 5)
    struct hacker
    {
    	int q;
    };
    tmp c = {5};
    reinterpret_cast<hacker*>(&c)->q = 42;
    
    // 6)
    tmp d = {5};
    memcpy(&d, &h, sizeof(h));
    
    std::cout <<   m << " " <<   o << " " << a.m << std::endl;
    std::cout << b.p << " " << c.p << " " << d.p << std::endl;
    


  • Nexus schrieb:

    Der Const-Cast ist mehr ein Workaround für bereits schlechtes Design als ein wirklich oft sinnvoll eingesetztes Sprachmittel. Was hat Const-Correctness für einen Sinn, wenn man sich nicht daran hält? Wenn man ein Objekt als const deklariert, geht man davon aus, dass es nicht geändert wird. Daran kann man sich gefälligst halten...

    stimmt, aber in const-korrektem code darfste nie einen const-pointer an eine nicht-const funktion übergeben, obwohl du 100% weisst, dass sie deine daten nicht anrührt. das ist eine grosse einschränkung. dann vielleicht besser ganz auf const verzichten. ich z.b. verwende 'const' nur, um compiler und linker unter die arme zu greifen, um (auf 'nem embedded system mit wenig RAM) dafür zu sorgen, das unveränderliche daten im Flash und nicht im RAM landen.

    Nexus schrieb:

    Gibt noch ein paar Möglichkeiten, besonders mit Strukturen (obwohl diese sehr ähnlich sind).

    1,2 und 3 kann ich nicht gelten lassen, weil die auch daneben zielen können. 5 hat 'nen cast drin, aber kein 'const-cast', d.h. hb's verbotsliste könnte jetzt so aussehen:
    kündigungsgründe für uneinsichtige c++ hacker sind:
    1. alle casts
    2. memcpy
    3. unions
    4. placement new
    5. anlegen eigener strukturen
    ^^ und da wir das ja nicht wollen, schlagen wir am besten den GCC-entwicklern vor, dieses feature aufzunehmen. der switch könnte heissen: '-enable_coughbear_blacklist' *fg*
    🙂



  • ;fricky schrieb:

    hustbaer schrieb:

    ;fricky schrieb:

    hustbaer schrieb:

    ok, fricky, sorry. mein fehler.
    kreativen const-schummel code zu schreiben ist natürlich produktiver als sauber zu programmieren. wie konnte ich nur was anderes glauben.

    wieso 'schummel'?

    const wegcasten, und dann etwas modifizieren, ist schummeln.

    nö, wenn du z.b. 'ne funktion aufrufen willst, bei der das 'const' fehlt, die aber definitiv keine schreibzugriffe über diesen pointer macht, musstes wegcasten. das ist nichts schlimmes und funktioniert auch.

    dann ist es aber auch kein problem, und man kann sich weiterhin auf const verlassen. genau das hast du ja bestritten, und ich versuche dir zu erklären, dass es unfug ist so zu programmieren, dass man sich nicht darauf verlassen kann. lies mal auf seite 6 wenn deine erinnerung nichtmehr ausreicht.

    hustbaer schrieb:

    schnittstellen müssen sowieso gut dokumentiert sein, wenn andere damit arbeiten sollen. 'const' hilft dabei nur bedingt.

    was heisst bedingt? bedingt wie "hilft viel, aber ist nicht 100% ausreichend"? oder bedingt wie "ist so-gut-wie egal"? sag mal konkret was du meinst.

    das kommt drauf an. wenn 'const' logisch passt, unterstützt es die lesbarkeit. wenn aber z.b. da steht: 'void funktion (const void *p)', dann ist klar, dass das interface irgendwie mist ist, weil mit ziemlicher sicherheit im innern gecastet wird.

    klar wird vermutlich irgendwo gecastet, aber das const wird hoffentlich nicht weggecastet.

    btw, nochwas zu deinen casting-verbotsregeln 'wer const wegcastet wird gefeuert'

    hab ich nie geschrieben.

    eine kleine quizfrage: wie bekommt man 'const' weg, ohne irgendeinen cast zu benutzen.?

    irrelevant, da das noch viel schlimmere hacks sind, als const wegcasten. wer sowas schreibt, nur um irgendwelche regeln zu umgehen, gehört erst recht entlassen.



  • ;fricky schrieb:

    Th69 schrieb:

    Gar nicht(!)

    doch, 'entconsten' geht ohne cast. wer errät den trick (ist garnicht so schwer)?
    🙂

    einfacher:

    #define const
    
    void foo(const int* v)
    {
      *v = 5;
    }
    


  • tntnet schrieb:

    #define const
    
    void foo(const int* v)
    {
      *v = 5;
    }
    

    ^^auch nicht schlecht, aber manche compiler lassen's nicht zu, wenn man schlüsselwörter mit #define verändern will.
    🙂



  • ;fricky schrieb:

    kündigungsgründe für uneinsichtige c++ hacker sind:
    1. alle casts
    2. memcpy
    3. unions
    4. placement new
    5. anlegen eigener strukturen

    oder:

    kündigungsgrund für uneinsichtige c++ hacker ist:

    Code der undefiniertes Verhalten beinhaltet oder auslösen kann.

    (und dazu gehört: innerhalb der Funktion einem Argument das const wegcasten und reinschrieben)



  • ;fricky schrieb:

    tntnet schrieb:

    #define const
    
    void foo(const int* v)
    {
      *v = 5;
    }
    

    ^^auch nicht schlecht, aber manche compiler lassen's nicht zu, wenn man schlüsselwörter mit #define verändern will.
    🙂

    Wie schaffen die das? Es ist doch der Präprozessor, der die Ersetzung macht. Der Compiler sieht das doch gar nicht mehr.

    Übrigens noch übler finde ich:

    #define private public
    #include <someclassfile.h>
    

    Das habe ich mal im echten Code gesehen. Wäre ein Fall für Daily WTF.



  • Das habe ich mal im echten Code gesehen. Wäre ein Fall für Daily WTF.

    Ich hoffe jetzt mal, dass du den dafür zuständigen Programmierer aufgespürt hast und ihm ordentlich eine verpasst hast.

    Oder noch besser mit ihm das hier gemacht:

    #define zustaendiger_programmierer void
    

Anmelden zum Antworten