- Krishtys schöne „Von-rechts-nach-links-Leseregel“ funktioniert leider nicht, sobald man Arrays benutzt:
- (&i)[3] ist eine Referenz auf ein 3-Array von ints,
- &(i[3]) ist ein 3-Array von Referenzen auf ints.
- Die Notation von Funktionen, die eine Referenz oder einen Pointer auf ein Array zurückgeben, ist relativ archaisch:
Das ganze gibt's hier auch in Aktion zu sehen.
Code: Alles auswählen
// Gibt eine Referenz auf ein 3-Array von ints zurück int (& getThreeInts()) [3] { static int theInts[3] = { 1, 2, 3 }; return theInts; }
- Ich hatte mich schon einmal hier mit dem Zusammenspiel von Array-Referenzen und Templates befasst. Wie mir heute plötzlich aufgefallen ist, kann man das generalisieren und für eine Variante des „wie groß ist mein Array“-Problems benutzen:
Code: Alles auswählen
template <typename theType, size_t theSize> char (& AsArray(theType (&) [theSize])) [theSize];
(Auch hier live und in Farbe). Im Gegensatz zu sizeof(…)/sizeof(…[0]) funktioniert es korrekterweise nicht mit Pointern. Es ist somit tatsächlichCode: Alles auswählen
struct { int i; double d; } array[10]; double * notAnArray; // Funktioniert. std::cout << sizeof(AsArray(array)) << std::endl; std::cout << sizeof(array) / sizeof(array[0]) << std::endl; // Funktioniert nicht; soll es auch nicht! // std::cout << sizeof(AsArray(notAnArray)) << std::endl; // Funktioniert; undefiniertes Verhalten. std::cout << sizeof(notAnArray) / sizeof(notAnArray[0]) << std::endl;
der Held den Gotham brauchttypsicherer als das sizeof(…)/sizeof(…[0])-Konstrukt.
Spaß mit Array-Typbezeichnern
Spaß mit Array-Typbezeichnern
Ich komme gerade von einer meiner Expedition aus den C++-Gebieten oberhalb des nördlichen Polarkreises persönlichen Erkenntniskreises zurück. Mein Logbuch:
- CodingCat
- Establishment
- Beiträge: 1857
- Registriert: 02.03.2009, 21:25
- Wohnort: Student @ KIT
- Kontaktdaten:
Re: Spaß mit Array-Typbezeichnern
Deshalb typedef oder std::array, soweit möglich (C-Arrays lassen sich obendrein nicht kopieren).eXile hat geschrieben:Die Notation von Funktionen, die eine Referenz oder einen Pointer auf ein Array zurückgeben, ist relativ archaisch:Das ganze gibt's hier auch in Aktion zu sehen.Code: Alles auswählen
// Gibt eine Referenz auf ein 3-Array von ints zurück int (& getThreeInts()) [3] { static int theInts[3] = { 1, 2, 3 }; return theInts; }
Worauf du hier hinaus willst, ist mir nicht ganz klar. Dieser ganze sizeof-Kram sollte eigentlich überhaupt nie mehr vorkommen. Warum die Standard-Bibliothek noch immer keine arraylen-Funktion anbietet, ist mir genauso ein Rätsel wie ihre kryptische, streng iteratorbasierte Algorithmenschnittstelle.eXile hat geschrieben:Ich hatte mich schon einmal hier mit dem Zusammenspiel von Array-Referenzen und Templates befasst. Wie mir heute plötzlich aufgefallen ist, kann man das generalisieren und für eine Variante des „wie groß ist mein Array“-Problems benutzen:
[...]
(Auch hier live und in Farbe). Im Gegensatz zu sizeof(…)/sizeof(…[0]) funktioniert es korrekterweise nicht mit Pointern. Es ist somit tatsächlichder Held den Gotham brauchttypsicherer als das sizeof(…)/sizeof(…[0])-Konstrukt.
Code: Alles auswählen
template <class T, size_t N>
inline size_t arraylen(T (&)[N]) // constexpr?
{
return N;
}
Code: Alles auswählen
// Funktioniert.
std::cout << arraylen(array) << std::endl;
// Funktioniert nicht; soll es auch nicht!
std::cout << arraylen(notAnArray) << std::endl;
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
- CodingCat
- Establishment
- Beiträge: 1857
- Registriert: 02.03.2009, 21:25
- Wohnort: Student @ KIT
- Kontaktdaten:
Re: Spaß mit Array-Typbezeichnern
Wenn das heißt, dass obige arraylen()-Funktion nicht als constexpr durchgeht, wäre dieses Feature aber sehr unglücklich ausgefallen. Mir ist ohnehin nicht klar, wieso es nicht Aufgabe des Compilers ist, inline-Funktionen einfach bei Aufruf nach Inlining auf constexpr-Konformanz zu prüfen (constexpr impliziert notwendigerweise immer inline).C++11 hat geschrieben:The definition of a constexpr function shall satisfy the following constraints:
[...]
— each of its parameter types shall be a literal type;
[...]
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Re: Spaß mit Array-Typbezeichnern
Ich wollte eigentlich auf gar nichts hinaus. ;) Ich fand es nur interessant, dass obiger Code von mir tatsächlich funktioniert; ich finde ihn ganz hübsch, was natürlich eine viel zu subjektive Einschätzung ist. :)CodingCat hat geschrieben:Worauf du hier hinaus willst, ist mir nicht ganz klar.
Naja, vielleicht doch, wenn auch nur sehr, sehr selten.CodingCat hat geschrieben:Dieser ganze sizeof-Kram sollte eigentlich überhaupt nie mehr vorkommen.
Machen wir halt ein ganz konkretes Beispiel. Stell dir mal vor, du hast ein Modell, das ein ganz bestimmtes Input-Layout hat, und CreateInputLayout erwartet die Größe des übergebenen D3D11_INPUT_ELEMENT_DESC-Arrays. Leider geht
Code: Alles auswählen
std::array<D3D11_INPUT_ELEMENT_DESC> inputLayout = {1, 2, 3};
Code: Alles auswählen
std::array<D3D11_INPUT_ELEMENT_DESC, 3> inputLayout = {1, 2, 3};
Angenommen, das ganz bestimmte Input-Layout des Models ändert sich: Ein Element fliegt raus. Schnell geändert, und Fehler gemacht:
Code: Alles auswählen
std::array<D3D11_INPUT_ELEMENT_DESC, 3> inputLayout = {1, 2};
Stattdessen schreiben wir
Code: Alles auswählen
D3D11_INPUT_ELEMENT_DESC inputLayout[] = {1, 2};
Zugegeben, dieses Beispiel funktioniert nur bei nicht-generischen Implementierungen (beispielsweise wenn ich mir das Input-Layout durch den Modell-Lader vorschreiben lasse, ist das dann ein vector); aber manchmal kommen eben auch so nicht-generische Implementierungen vor. Und ebenfalls zugegeben: Die Abhängigkeit ist extrem Code-lokal, also nicht sehr schlimm.
CodinCat hat geschrieben:Warum die Standard-Bibliothek noch immer keine arraylen-Funktion anbietet, ist mir genauso ein Rätsel wie ihre kryptische, streng iteratorbasierte Algorithmenschnittstelle.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf hat geschrieben:The definition of a constexpr function shall satisfy the following constraints:
[…]
— its return type shall be a literal type;
— each of its parameter types shall be a literal type;
[…]
Und da das formal eine Referenz auf ein Array ist (und damit ja auch nicht zu einem Pointer verflacht werden kann!) sollte das keine Probleme geben.http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf hat geschrieben:A type is a literal type if it is:
[…]
— a reference type referring to a literal type; or
— an array of literal type; or
[…]
Und ja, man sollte so etwas wie arraylen in die STL aufnehmen.
- CodingCat
- Establishment
- Beiträge: 1857
- Registriert: 02.03.2009, 21:25
- Wohnort: Student @ KIT
- Kontaktdaten:
Re: Spaß mit Array-Typbezeichnern
Naja, diese Dopplung sizeof(AsArray(...)) erscheint mir nicht viel weniger fehleranfällig als sizeof(...) / sizeof(...). In beiden Fällen führt das intuitive Weglassen der zweiten Hälfte zu nicht zur Compile-Zeit erkennbarem Fehlverhalten. (Obendrein ist sizeof(AsArray(...)) intuitiv absolut unverständlich?! ;))eXile hat geschrieben:Ich wollte eigentlich auf gar nichts hinaus. ;) Ich fand es nur interessant, dass obiger Code von mir tatsächlich funktioniert; ich finde ihn ganz hübsch, was natürlich eine viel zu subjektive Einschätzung ist. :)
Das "überhaupt nie" bezog sich nicht auf std::array, sondern auf konsequentes arraylen() statt sizeof()-Gepfriemel.eXile hat geschrieben:Naja, vielleicht doch, wenn auch nur sehr, sehr selten. Machen wir halt ein ganz konkretes Beispiel. [...]CodingCat hat geschrieben:Dieser ganze sizeof-Kram sollte eigentlich überhaupt nie mehr vorkommen.
Aber wieso sizeof(asArray(inputLayout))?! Stattdessen schreiben wir arraylen(inputLayout), noch eine Fehlerquelle weniger. ;) In den meisten Fällen ist constexpr ohnehin nicht notwendig.eXile hat geschrieben:Stattdessen schreiben wirund übergeben als Array-Größe sizeof(asArray(inputLayout)). Keine Abhängigkeit mehr.Code: Alles auswählen
D3D11_INPUT_ELEMENT_DESC inputLayout[] = {1, 2};
Höchst kurios, ich habe hier einen aktuellen Working Draft (N3337), in dem der Zusatz mit den Referenzen jeweils fehlt. Im (älteren?) C++11 Final Draft hingegen finde ich genau das, was du hier zitierst.eXile hat geschrieben:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf hat geschrieben:The definition of a constexpr function shall satisfy the following constraints:
[…]
— its return type shall be a literal type;
— each of its parameter types shall be a literal type;
[…]Und da das formal eine Referenz auf ein Array ist (und damit ja auch nicht zu einem Pointer verflacht werden kann!) sollte das keine Probleme geben.http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf hat geschrieben:A type is a literal type if it is:
[…]
— a reference type referring to a literal type; or
— an array of literal type; or
[…]
Und ja, man sollte so etwas wie arraylen in die STL aufnehmen.
Nachtrag: Die Definition von "literal type" schließt im aktuellen Working Draft schon Referenzen auf Literaltypen mit ein, deshalb wurde der zitierte Paragraph entsprechend verkürzt.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Re: Spaß mit Array-Typbezeichnern
Außer natürlich für Pointer, aber das hatten wir ja schon.CodingCat hat geschrieben:Naja, diese Dopplung sizeof(AsArray(...)) erscheint mir nicht viel weniger fehleranfällig als sizeof(...) / sizeof(...).
CodingCat hat geschrieben:In beiden Fällen führt das intuitive Weglassen der zweiten Hälfte zu nicht zur Compile-Zeit erkennbarem Fehlverhalten.
CodingCat hat geschrieben:Obendrein ist sizeof(AsArray(...)) intuitiv absolut unverständlich?! ;)
Jajaja, die Benennung ist scheiße, und auch mit dem weglassen hast du recht. Ich wollte noch eine Variante, die auch zur Kompilierzeit funktioniert (und constexpr ist in Visual C++ ja noch nicht drin). Aber du hast recht, die Dopplung ist absolut unnötig und gehört weggekapselt in einer Funktion. ;)CodingCat hat geschrieben:Stattdessen schreiben wir arraylen(inputLayout), noch eine Fehlerquelle weniger. ;)
Achso, ja, OK.CodingCat hat geschrieben:Das "überhaupt nie" bezog sich nicht auf std::array, sondern auf konsequentes arraylen() statt sizeof()-Gepfriemel.
Als generelle Frage: Was sagt der Standard eigentlich zur Optimierung? Tangiert den das gar nicht, oder gibt es irgendwelche Garantien, dass kein Funktionsaufruf generiert wird? Ich nehme mal an, Visual C++ wird das ohne Probleme zur Kompilierungszeit auflösen.CodingCat hat geschrieben:In den meisten Fällen ist constexpr ohnehin nicht notwendig.
- Krishty
- Establishment
- Beiträge: 8350
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Spaß mit Array-Typbezeichnern
Am Arsch – Visual C++ ist manchmal so dumm, dass ich nur noch in Großbuchstaben programmieren will. Für den Fall von arraylen klappt es definitiv, ja. Aber glaub ja nicht, dass die Funktion, die den Wert als Parameter erwartet, den als constexpr weiterbenutzt – sogar dann, wenn sie komplett geinlinet wird, ist das bloßes Glücksspiel.
Ich war ja vor ein paar Monaten nicht mehr aus dem Jammern rausgekommen, weil ich eine Funktion hatte, die einen String-Hash berechnet. Da war die String-Länge ebenfalls so aufgelöst; die Funktion wurde ein paar tausend Mal geinlinet; aber innendrin wurde nichts weiter von der Konstanten aufgelöst.
Ich war ja vor ein paar Monaten nicht mehr aus dem Jammern rausgekommen, weil ich eine Funktion hatte, die einen String-Hash berechnet. Da war die String-Länge ebenfalls so aufgelöst; die Funktion wurde ein paar tausend Mal geinlinet; aber innendrin wurde nichts weiter von der Konstanten aufgelöst.
- CodingCat
- Establishment
- Beiträge: 1857
- Registriert: 02.03.2009, 21:25
- Wohnort: Student @ KIT
- Kontaktdaten:
Re: Spaß mit Array-Typbezeichnern
Wohl eher in ein Makro dann. Als Makro hat deine Variante tatsächlich Vorteile, weil sie prä-C++11 vollen Syntax-Komfort in Compile-Zeit- wie Laufzeitkontexten bietet.eXile hat geschrieben:Ich wollte noch eine Variante, die auch zur Kompilierzeit funktioniert (und constexpr ist in Visual C++ ja noch nicht drin). Aber du hast recht, die Dopplung ist absolut unnötig und gehört weggekapselt in einer Funktion. ;)
Code: Alles auswählen
template <typename T, size_t N>
char (& deduce_same_extent_char_array_type(T (&)[N]) )[N];
#define arraylen(a) sizeof(deduce_same_extent_char_array_type(a))
In prä-constexpr
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Re: Spaß mit Array-Typbezeichnern
Grandios. Warum ist Visual C++ nur so retardiert. Das wirft ganze Generationen von Programmierern zurück. Unabhängig von Thema; es sind ja solche Sachen, die mein Blut zum Kochen bringen:Krishty hat geschrieben:Am Arsch – Visual C++ ist manchmal so dumm, dass ich nur noch in Großbuchstaben programmieren will. Für den Fall von arraylen klappt es definitiv, ja. Aber glaub ja nicht, dass die Funktion, die den Wert als Parameter erwartet, den als constexpr weiterbenutzt – sogar dann, wenn sie komplett geinlinet wird, ist das bloßes Glücksspiel.
Ach, und wie? Genau!http://blogs.msdn.com/b/vcblog/archive/2012/06/15/10320846.aspx hat geschrieben:STL Bugs Fixed In Visual Studio 2012
The <initializer_list> header in the VC10 release candidate
:evil:http://connect.microsoft.com/VisualStudio/feedback/details/533464 hat geschrieben:We've fixed it by deleting <initializer_list> from VC11.
Hervorragend! Damit hat sich der Thread sich tatsächlich für mich sehr gelohnt, da mir std::extend noch gänzlich unbekannt war. Danke!CodingCat hat geschrieben:In prä-constexprhalb-sechzehntel-C++11-Compilern wie MSVC10+ gibt es btw. auch das etwas umständlichere std::extent<decltype(a)>::value für Compile-Zeit-Kontexte.
- dot
- Establishment
- Beiträge: 1746
- Registriert: 06.03.2004, 18:10
- Echter Name: Michael Kenzel
- Kontaktdaten:
Re: Spaß mit Array-Typbezeichnern
std::extend kannte ich auch noch nicht. Ich frag mich allerdings, wieso std::extend für non-array Typen 0 liefert; Imo wäre die einzig vernünftige Lösung, das Template nur für Arrays zu spezialisieren und sonst einfach undefiniert zu lassen!?
Zum Thema InputLayout: Ich löse das normalerweise so, dass ich das Erzeugen eines InputLayout der jeweiligen Geometry Klasse überlasse (die, die die VertexBuffer hält):
Ist rein logisch imo dort am besten aufgehoben (wer die Buffer erzeugt und den Draw Call absetzt, kennt naturgemäß das exakte Layout; niemand sonst muss es kennen) und dann brauchts auch nur ein einfaches Array in der Funktion...
Zum Thema InputLayout: Ich löse das normalerweise so, dass ich das Erzeugen eines InputLayout der jeweiligen Geometry Klasse überlasse (die, die die VertexBuffer hält):
Code: Alles auswählen
virtual com_ptr<ID3D11InputLayout> createInputLayout(device, shadersignature) const = 0;
Die ist ja auch nur ein Spezialfall der richtigen Regel, die da lautet: Von Innen nach Außen ;)eXile hat geschrieben:Krishtys schöne „Von-rechts-nach-links-Leseregel“ funktioniert leider nicht, sobald man Arrays benutzt: