Das dieses nicht hinhauen kann ist mir natürlich klar, da der Compiler
beim kompilieren nicht wissen kann, wie groß das Array element
tatsächlich ist, da er nur weiß, dass es ein Pointer auf ein element_t
ist und sich drauf verlassen soll, dass der Speicherbereich vom Linker
schon noch dazugelinkt wird.
Jetzt stellt sich für mich nur die Frage wie man es sinnvoll besser
machen kann. Das Beispiel ist wirklich nur als Beispiel gedacht, in
Wirklichkeit ist das Strukt komplexer und die source.c variabel und je
nach Konfiguration anders.
Ursprünglich war der Inhalt der source.c mit in der main.c und da gabs
natürlich kein Problem, nur die Defines für die verschiedenen
Konfigurationen wurden immer länger, so dass diese jetzt in seperate
Header und Source Dateien verteilt ist.
Als erste Idee kam mir dazu in den Sinn einfach ein Struct zu definieren
mit einem Pointer auf element_t und der Anzahl der Elemente:
header.h:
Irgendwie ist das die gleiche Lösung wie mit dem Struct nur das es eben
einfach als globale Variable drin liegt und ich für jede Art von
Elementen eine eigene extern Deklaration machen muss anstatt diese
gesammelt in einem (config_t) struct zu machen.
Es ist insofern nicht gleich, als es kompiliert und läuft.
Natürlich kannst du nach demselben Strickmuster auch statt einer size_t
eine struct mit mehreren machen.
PS: in meinem Vorschlag fehlt bei nElemente noch jeweils ein const in
der .h und der .c.
Klaus W. schrieb:> Es ist insofern nicht gleich, als es kompiliert und läuft.
Naja, bis auf die ein oder andere Kleinigkeit welche dem freien
schreibseln hier im Editor geschuldet ist, läuft meine 2. Variante ja
auch, das war nie die Frage.
Klaus W. schrieb:> Deinen Vorschlag mit der struct finde ich für C durchaus angemessen.>> Wesentlich eleganter geht es dann wohl erst in C++...
Ja, das Struct kommt halt am nächsten an eine Klasse ran. Ich hatte halt
überlegt ob es mit kreativem Header und/oder Präprozessor Missbrauch
nicht noch eine Lösung dafür geben könnte.
PS: Hab mal die funktionierende Beispiellösung mit dem Struct angehängt,
das funktioniert wie gesagt und kompiliert auch mit -Wall, -Wextra und
-pedantic ohne zu murren.
Aus dem typedef für element_t würde ich die const von a und b wegnehmen;
du machst ja nachher eh das ganze Feld nochmal const. Noch conster geht
nicht.
Sonst würde ich da nicht mehr viel verpfuschen.
Tricksen kann man noch viel, das macht es aber nicht unbedingt besser
oder gar verständlicher.
> Ich hatte halt überlegt ob es mit kreativem Header und/oder> Präprozessor Missbrauch nicht noch eine Lösung dafür geben könnte.
Ich würde das nur weiter treiben, wenn ich die Größe in den anderen
Dateien als (compile-time-)Konstante bräuchte. Das bedeutet, die Größe
steht in einem separatem .h-File, das im Buildprozesses nach dem
Kompilieren von source.c (denn erst danach ist die tatsächliche Größe ja
bekannt) automatisiert erstellt/aktualisiert wird. Verlangt dann ein
paar Tricksereien im Buildsystem (extra Regeln/Abhängigkeiten,
extrahieren der Größe aus dem .o-File, etc), die man nur macht, wenn's
unbedingt nötig ist.
In C zerfallen die Array-Bezeichner (fast) immer (wie in diesem Fall) zu
einem Zeiger (decay of array designator). Das geht nur in der
Übersetzungseinheit, in dem der Compiler die vollständige Deklaration
des Array-Bezeichners gesehen hat. Demzufolge geht es eben nicht
zwischen Übersetzungseinheiten bzw. bei Parameterlisten.
Da hier schon C++ angesprochen wurde: daher kommt auch die Äußerung von
Bjarne: C-Arrays sind so dummm, sie kennen noch nicht einmal ihre Länge.
Ist sofern ist die Lösung mit dem eigenen Datentyp (struct) schon ok.
Und bei Parameterlisten auch die Lösung mit einem zusätzlichen Parameter
für die Länge. Beides allerdings bei statischen Arrays Krücken. Aber so
ist C nun mal ...
Die Variante mit einem CPP-Macro ist natürlich wie immer die
schlechteste Variante.
Aktuell exponiert deine source.c (bzw. dessen header) nur ein Symbol,
nämlich extern const element_t element[];
Das reicht wie du festgestellt hast nicht, du musst also ein zweites
Symbol exponieren, nämlich die Länge von element[].
Jetzt hast du zwei Möglichkeiten:
- per Makro
- per Variable und forward declaration
Die Makro-Lösung hat den Nachteil dass du die Länge hartcodiert angeben
musst, also
#define len 3
Spielereien mit sizeof() im Makro werden nicht funktionieren da in
main.c die Arrays ja bereits zerfallen sind.
Also musst du das Makro händisch aktuell halten.
Deswegen würde ich hier eine Variable "element_len" exponieren. Wenn die
Variable innerhalb von source.c angelegt wird kannst du da auch mit
sizeof() arbeiten. Im HEader hast du dann nur noch eine forward
declaration
Ob du eine losgelöste Variable exponierst oder die Infos in einem struct
sammelst ist dann syntaktischer Zucker, kommt auf deinen Gusto an.
Le X. schrieb:> Die Makro-Lösung hat den Nachteil dass du die Länge hartcodiert angeben> musst, also> #define len 3> Spielereien mit sizeof() im Makro werden nicht funktionieren da in> main.c die Arrays ja bereits zerfallen sind.> Also musst du das Makro händisch aktuell halten.
Naja, das ist ja nicht der Nachteil der Macro-Lösung: Du kannst das
Macro ja auch in der Definition des Arrays verwenden, damit hast Du
keine Duplikation.
Der Nachteil der CPP-Macros ist, dass sie nicht ge-scoped sind und
einfach nur Textersatz, können also an beliebigen Stellen "zuschlagen".
> Ob du eine losgelöste Variable exponierst oder die Infos in einem struct> sammelst ist dann syntaktischer Zucker, kommt auf deinen Gusto an.
Nein, das ist kein syntaktischer Zucker, sondern - im Gegenteil - ein
ganz wichtiger Vorteil: denn mit einem struct verknüpfst Du
zusammengehörige Informationen zu einer Entität. Leider ist es in C
nicht ganz, nur fast ein Datentyp, da man freie Funktionen nicht
überladen kann und es keine Elementfunktionen gibt.
Klaus W. schrieb:> Aus dem typedef für element_t würde ich die const von a und b wegnehmen;> du machst ja nachher eh das ganze Feld nochmal const. Noch conster geht> nicht.
Das eigentliche Problem daran ist, dass die Elemente damit nicht mehr
zuweisbar sind.
Damit
- bleiben Objekte vom Type element_t zuweisbar (das wird wohl in Deinem
Fall sinnvoll sein),
- der Zieltyp (pointee) bleibt änderbar in config_t,
- der Zeiger selbst (pointer) wie auch die zugehörige Länge sind
immutable,
- was dazu führt, dass die Invariante nicht nachträglich verletzt werden
kann,
- der Datentyp für Anzahl ist korrekt (nicht-negativ und nicht zu
klein).
Wilhelm M. schrieb:> Das eigentliche Problem daran ist, dass die Elemente damit nicht mehr> zuweisbar sind
Bei der Definition sind sie bekannt. Das reicht, da können sie
zugewiesen werden.
Die wichtigere Frage: wofür brauchst Du die Anzahl?
Und warum ist sie vorher nicht bekannt?
Am Ende ist es recht selten, wo man "nur" die Anzahl braucht. Oft
braucht man noch weitere Infos im struct oder eine terminierte Liste ist
ausreichend.
A. S. schrieb:> Die wichtigere Frage: wofür brauchst Du die Anzahl?
Meistens möchte man drüber iterieren.
> Und warum ist sie vorher nicht bekannt?
Doch, das ist sie ja.
> Am Ende ist es recht selten, wo man "nur" die Anzahl braucht. Oft> braucht man noch weitere Infos im struct oder eine terminierte Liste ist> ausreichend.
Natürlich kann er auch ein Sentinel verwenden, dazu müsste der TO
verraten, ob es bei den Elementen des struct einen ungültigen Wert gibt,
den man als Sentinel verwenden kann.
Tim T. schrieb:> Das dieses nicht hinhauen kann ist mir natürlich klar, da der Compiler> beim kompilieren nicht wissen kann, wie groß das Array element> tatsächlich ist, da er nur weiß, dass es ein Pointer auf ein element_t> ist und sich drauf verlassen soll, dass der Speicherbereich vom Linker> schon noch dazugelinkt wird.
Der Code funktioniert doch problemlos? Der Compiler muss nur die
Definition
sehen. Da das Array sowieso const ist, kannst du es auch einfach
inkludieren,
eventuell ein inline davor setzen.
A. S. schrieb:> Wilhelm M. schrieb:>> Das eigentliche Problem daran ist, dass die Elemente damit nicht mehr>> zuweisbar sind>> Bei der Definition sind sie bekannt. Das reicht, da können sie> zugewiesen werden.
Das ist keine Zuweisung, sondern eine Initialisierung.
const-Objekte sind read-only und können nicht zugewiesen werden.
In c steht hinter dem Namen immer eine Adresse (Zeiger). Die maximale
Größe hängt vom adressierbaren Speicherraum des Prozessors ab und
dadurch letzten Endes vom Compiler.
Gerald K. schrieb:> In c steht hinter dem Namen immer eine Adresse (Zeiger). Die maximale> Größe hängt vom adressierbaren Speicherraum des Prozessors ab und> dadurch letzten Endes vom Compiler.
Das ist falsch, wie ich oben schon geschrieben habe: array-Bezeichner
sind (abstrakte) array-Bezeichner, Zeiger sind Zeiger, und beides
erstmal unterschiedliche Konzepte. In vielen Kontexten zerfallen
allerdings array-Bezeichner zu einem Zeiger auf das erste Element.
Ich programmiere C/++ seit 30 Jahren... und ich mag die Sprache da man
sehr nah an der Hardware ist. Die (redundanten!) Header haben mich
allerdings im Laufe der Zeit immer mehr gestört. Und schlimmer, wie hier
im Thread (initializer lists), manchmal geben sie nicht mal die
Möglichkeit her, die Definition mit der Deklaration vernünftig zu
verheiraten.
Der mit C++20 eingeführte export ist mehr als überfällig.
Zurück zum Thema, hier noch eine (eher akademische) Alternative:
1
$ cat foo.h
2
typedef struct {
3
int a;
4
int b;
5
} element_t;
6
7
enum {
8
nelements = 3 // macros are evil
9
};
10
11
extern element_t const (*elements) [nelements];
12
// pointer so the compiler can implicitly type check
Wilhelm M. schrieb:> Ich würde es in der Tat so machen:> typedef struct {> int a;> int b;> } element_t;> typedef struct {> element_t* const element;> const size_t anzahl;> } c
Dann musst du auch die dazugehörigen Funktionen anlegen, die dir neue
Elemente von diesem Datentyp anlegen UND gleichzeitig den Counter
(automatisch) inkrementieren. Dann ist man gleich bei Getter und Setter.
C kennt die Arraygröße. Dort, wo Array angelegt wird, dort soll man auch
seine Größe ermitteln. Und dort, wo Array bekannt gemacht wird (per
extern), dort wird auch seine Größe (Größenvariable) (per extern)
bekannt gemacht. Fertig.
Klaus W. schrieb:> source.c:#include "header.h"> const element_t element[] = { {1,2}, {3,4}, {5,6} };> size_t nElemente = sizeof(element)/sizeof(element[0]);
kein Problem und bereits gelöst schrieb:> Dann musst du auch die dazugehörigen Funktionen anlegen, die dir neue> Elemente von diesem Datentyp anlegen UND gleichzeitig den Counter> (automatisch) inkrementieren. Dann ist man gleich bei Getter und Setter
Da es um statische Arrays geht, ist das Inkrementieren der Anzahl
sinnlos (und deswegen hier nicht möglich).
kein Problem und bereits gelöst schrieb:> C kennt die Arraygröße. Dort, wo Array angelegt wird, dort soll man auch> seine Größe ermitteln.
Das tut er ja auch oben. Alles gut.
> Und dort, wo Array bekannt gemacht wird (per> extern), dort wird auch seine Größe (Größenvariable) (per extern)> bekannt gemacht. Fertig.
Nix fertig.
Es geht doch um Kontexte, wo die Definition nicht mehr sichtbar ist und
der Bezeichner zu einem Zeiger zerfallen ist (Standardbeispiel:
Parameterlisten).
fiju schrieb:> Die Anzahl der Elemente in die Struc mit aufnehmen>> typedef struct {> int Anzahl> int a;> int b;> } element_t;
So nach dem Motto, n-fach gemoppelt hält besser ;-)
Schwachsinn! Wie der Name schon sagt, ist element_t der DT eines
Elementes(!), nicht des Containers!
Udo K. schrieb:> So geht es auch:
Das ist der straight-forward Ansatz. Die Elemente in der initializer
list abzählen und das Array entsprechend deklarieren.
Die implizite (böse) Redundanz ist aber ein Problem: Was wenn man sich
verzählt hat oder wenn nachträglich die initializer list geändert wird?
Ist die initializer list größer, dann gibt der Compiler bescheid.
Ist die initializer list kleiner, werden implizt Null Elemente
angehangen. Letzteres möchte man nicht unbedingt.
Beim meinem Ansatz erkennt der Compiler beide Probleme. (Eher
akademisch, denn wer möchte schont mit einem Pointer auf ein Array
arbeiten.)
Bevor das hier weiter ausartet:
In meinem Fall gibt es nicht eine source.c sondern knapp 20, von denen
aber immer nur genau eine über ein Define vom jeweiligen Compiler
eingebunden wird. Die knapp 20 Dateien sind für verschiedene
Mikrocontroller und beinhalten mögliche Konfigurationsvarianten einer
Software, die sich je nach Controller aber auch noch unterscheiden. Ein
element_t ist praktisch dabei eine einzelne Konfiguration von denen pro
Controller mehrere im zur Auswahl stehen, darum das Array von element_t;
welche und wie viele unterscheidet sich dabei ebenfalls je nach
Controller. Die Anzahl ist dabei relevant um beim "durchblättern" der
Konfigurationen zur Laufzeit nach dem letzten Element wieder von vorne
anfangen zu können, oder andersrum, vor dem ersten Element wieder zum
letzten zu springen. Ein statisches Festlegen der Anzahl wollte ich
vermeiden, da das Anpassen beim Hinzufügen oder Entfernen von
Konfigurationen schnell vergessen werden kann.
Mikro 7. schrieb:> Die implizite (böse) Redundanz ist aber ein Problem: Was wenn man sich> verzählt hat oder wenn nachträglich die initializer list geändert wird?
Alleine die Tatsache, dass man überhaupt von Hand abzählen muss, stört
schon. Wozu hat man den Computer?
Rolf M. schrieb:> Alleine die Tatsache, dass man überhaupt von Hand abzählen muss, stört> schon. Wozu hat man den Computer?
Eine der Schwächen von C/++. "export" in C++20 verspricht zumindest
etwas Besserung.
Tim T. schrieb:> Die Anzahl ist dabei relevant um beim "durchblättern" der> Konfigurationen zur Laufzeit nach dem letzten Element wieder von vorne> anfangen zu können, oder andersrum, vor dem ersten Element wieder zum> letzten zu springen.
Beim ersten sind terminierte Listen praktisch, sie halten den Code
einfach. Leider ist es fatal, wenn sie nicht terminiert sind.
Für einen "Roll-back" ist Deine Struct-Lösung deutlich besser. Von daher
hast Du alles richtig gemacht.
Sobald Du das struct einmal hast, wirst Du es vermutlich mit weiteren
Pointern füllen. Sei es auf Name oder SAP-Nummer der Platine, des
Controllers oder dessen generelle Möglichkeiten (also jene Dinge, die
nicht in jeder Konfiguration eines Controllers auftauchen müssen,
sondern nur einmal pro Set)
Für Laufzeit und Speicher spielen Indirektionen in den meisten Fällen
keine Rolle.
Mikro 7. schrieb:> Rolf M. schrieb:>> Alleine die Tatsache, dass man überhaupt von Hand abzählen muss, stört>> schon. Wozu hat man den Computer?> Eine der Schwächen von C/++. "export" in C++20 verspricht zumindest> etwas Besserung.
Soll es in C auch Module wie in C++ geben? Du meinst Module, wenn du
"export" schreibst?
Für C++ macht das kaum einen Unterschied, da man jederzeit ein
std::array benutzen könnte.
Rolf M. schrieb:> Alleine die Tatsache, dass man überhaupt von Hand abzählen muss, stört> schon. Wozu hat man den Computer?
Naja, abzählen muss man ja nicht. Und solange jede Einheit einzeln
kompiliert werden können soll, gibt es auch keine elegantere Lösung.
Höchstens Pattern, die das vor dem Anwender verstecken.
(Und natürlich kann man das auch in C den Linker machen lassen, über
Linker-Sections, ohne struct oder zählen. Aber wenn man den Code nicht
lesen können will, kann man besser C++ nehmen)
Mombert H. schrieb:> Für C++ macht das kaum einen Unterschied, da man jederzeit ein> std::array benutzen könnte.
Wie soll das mit std::array hier funktionieren?
Mikro 7. schrieb:> Mombert H. schrieb:>> Für C++ macht das kaum einen Unterschied, da man jederzeit ein>> std::array benutzen könnte.>> Wie soll das mit std::array hier funktionieren?
Das frage ich mich auch. Das Problem bleibt ja bestehen: Man muss im
Header immer noch die Größe von Hand angeben.
Rolf M. schrieb:> Mikro 7. schrieb:>> Mombert H. schrieb:>>> Für C++ macht das kaum einen Unterschied, da man jederzeit ein>>> std::array benutzen könnte.>>>> Wie soll das mit std::array hier funktionieren?>> Das frage ich mich auch. Das Problem bleibt ja bestehen: Man muss im> Header immer noch die Größe von Hand angeben.
Nein. Die Größe ist Bestandteil des Typs in diesem Fall.
Wilhelm M. schrieb:>> Das frage ich mich auch. Das Problem bleibt ja bestehen: Man muss im>> Header immer noch die Größe von Hand angeben.>> Nein.
Dann zeig mal, wie du ein std::array deklarierst, ohne die Größe
anzugeben.
> Die Größe ist Bestandteil des Typs in diesem Fall.
Das ist sie bei einem C-Array auch. Bei beiden muss ich sie bei einer
Deklaration explizit angeben, damit der Compiler weiß, wie groß es ist,
und zwar gerade weil die Größe Teil des Typs ist.
Konkret: Ob ich schreibe
1
externconstelement_telement[3];
oder
1
externconststd::array<element_t,3>element;
macht in der Hinsicht keinen Unterschied. Bei beiden steht die 3
explizit da. Also hilft mir std::array dabei nicht.
Oliver S. schrieb:> Rolf M. schrieb:>> Also hilft mir std::array dabei nicht.>> Dann nimm std::vector. Der kann für dich zählen.
Dann aber natürlich komplett zur Laufzeit, mit dynamischer Allokation
und allem, was da so dazu gehört.
Rolf M. schrieb:> Wilhelm M. schrieb:>>> Das frage ich mich auch. Das Problem bleibt ja bestehen: Man muss im>>> Header immer noch die Größe von Hand angeben.>>>> Nein.>> Dann zeig mal, wie du ein std::array deklarierst, ohne die Größe> anzugeben.>>> Die Größe ist Bestandteil des Typs in diesem Fall.> Also hilft mir std::array dabei nicht.
Mit CTAD kann die Größe und Elementtyp aus dem Initialisierer bestimmt
werden. Und ein std::array zerfällt nicht.
Einer der schönen Eigenschaften von C/++ sind die ausführlichen
Prüfungen zur compile time. Wo viele Sprachen auf einen runtime error
laufen, bietet C++ Möglichkeiten, einige davon bereits im Voraus
auszuschließen.
In diesem Sinn ist die Frage des TO interessant, weil C/++ hier
(initializer lists) durch seine fehlende "export" Möglichkeit und den
dadurch notwendigen verkappten Header declarations an seine Grenzen
stößt. Die Größe des Arrays ist zwar bekannt, man kann sie aber nicht
(zur compile time) exportieren.
Natürlich gibt es eine Menge Möglichkeiten damit in der Praxis
umzugehen. Die Größe händisch abzulegen ist wohl die praktikabelste (so
wie der TO es macht).
Rolf M. schrieb:> Mikro 7. schrieb:>> Mombert H. schrieb:>>> Für C++ macht das kaum einen Unterschied, da man jederzeit ein>>> std::array benutzen könnte.>> Wie soll das mit std::array hier funktionieren?> Das frage ich mich auch. Das Problem bleibt ja bestehen: Man muss im> Header immer noch die Größe von Hand angeben.
Wilhelm M. schrieb:> Mit CTAD kann die Größe und Elementtyp aus dem Initialisierer bestimmt> werden.
Auch das geht mit einem klassischen C-Array, wenn auch ganz ohne fancy
Akronym. Allerdings sollte dir eigentlich auch aufgefallen sein, dass es
hier um die extern-Deklaration im Header geht, die vom Initializer
nichts weiß.
Mombert H. schrieb:>> Das frage ich mich auch. Das Problem bleibt ja bestehen: Man muss im>> Header immer noch die Größe von Hand angeben.> #include <array>> struct element_t {> int a;> int b;> };> std::array element{element_t{1,2}, element_t{3,4}, element_t{5,6}};
Und wo ist jetzt die dazugehörige extern-Deklaration im Header, die ja
eigentlich das Thema der Diskussion ist?
Hier nochmal das Problem des TO:
Tim T. schrieb:> header.h:
1
typedefstruct{
2
constinta;
3
constintb;
4
}element_t;
5
externconstelement_telement[];// <- Es geht um diese Zeile
Rolf M. schrieb:> Und wo ist jetzt die dazugehörige extern-Deklaration im Header, die ja> eigentlich das Thema der Diskussion ist?
Dann schreib ein simples "constexpr inline" davor und pack die
Definition in den Header.
Mombert H. schrieb:> Rolf M. schrieb:>> Und wo ist jetzt die dazugehörige extern-Deklaration im Header, die ja>> eigentlich das Thema der Diskussion ist?> Dann schreib ein simples "constexpr inline" davor und pack die> Definition in den Header.
Und dann wird das Ding in 342 Quelldateien inkludiert und landet dann
342 mal im finalen Binary? Ob das im Sinne des TO ist wage ich zu
bezweifeln sonst hätte er das mit static const auch in C lösen können.
Μαtthias W. schrieb:> Mombert H. schrieb:>> Rolf M. schrieb:>>> Und wo ist jetzt die dazugehörige extern-Deklaration im Header, die ja>>> eigentlich das Thema der Diskussion ist?>> Dann schreib ein simples "constexpr inline" davor und pack die>> Definition in den Header.> Und dann wird das Ding in 342 Quelldateien inkludiert und landet dann> 342 mal im finalen Binary? Ob das im Sinne des TO ist wage ich zu> bezweifeln sonst hätte er das mit static const auch in C lösen können.
Nein, es landet eben nicht 342 mal im Binary. Das hat damit zu tun, dass
"constexpr inline" etwas anderes ist als "static const" ...
Rolf M. schrieb:> Und wo ist jetzt die dazugehörige extern-Deklaration im Header, die ja> eigentlich das Thema der Diskussion ist?
Die brauchen wir nicht mehr, wenn wir die kleinen Goodies von C++
verwenden.
Folgende Header-Datei:
Wilhelm M. schrieb:> Und ein std::array zerfällt nicht.
Wann will man das denn effektiv? Einfaches Szenario, ich will das
std::array an eine Funktion übergeben. Entweder die müssen immer gleich
gross sein, und ich muss die Grösse wissen, oder ich muss ein Template
nutzen, und habe dann vermutlich unnötigen Code. Oder ich übergebe halt
doch was anderes, als ein std::array, irgend ein Iterator oder so...
DPA schrieb:> Wilhelm M. schrieb:>> Und ein std::array zerfällt nicht.>> Wann will man das denn effektiv? Einfaches Szenario, ich will das> std::array an eine Funktion übergeben. Entweder die müssen immer gleich> gross sein, und ich muss die Grösse wissen, oder ich muss ein Template> nutzen, und habe dann vermutlich unnötigen Code. Oder ich übergebe halt> doch was anderes, als ein std::array, irgend ein Iterator oder so...
Immer will man das. Es sei denn, der DT der Elemente ermöglichen ein
Sentinel.
Ansonsten wäre man ja wieder bei dem C Scheiß.
Einige Anwendungsfälle erfordern ein Array einer vorgegebenen Größe (und
ggf. Elementtyp), dann ist es auch ein Vorteil, weil es bei einem
Mismatch zw. Argument- und Parametertyp nicht compiliert (im Gegensatz
zu C).
Siehe auch Algorithmen: die Algorithmen sind üblicherweise templates und
haben eine Schnittstelle mit
- Container,
- Iteratorpaar oder mehr,
- Range(s)
Und der angenommene Code-Bloat ist ein viel genanntes Pseudo-Argument,
was durch die bessere Optimierung oft (mehr als) wett gemacht wird.
Wilhelm M. schrieb:> Immer will man das. Es sei denn, der DT der Elemente ermöglichen ein> Sentinel. Ansonsten wäre man ja wieder bei dem C Scheiß.
Wenn die Funktion std::arrays mit unterschiedlichen Größen verarbeiten
können soll, bleibt mir wie DPA schon schreibt neben einem Template nur
die Möglichkeit, sowas wie einen Iterator zu nehmen. Wenn ich das tue,
bin ich aber tatsächlich da angelangt, wo C ist, denn das ist dann im
Prinzip das gleiche wie der Zeiger auf den Anfang des Arrays. An die
Größe komme ich wieder nicht heran, sofern sie nicht separat übergeben
wird.
> Und der angenommene Code-Bloat ist ein viel genanntes Pseudo-Argument,> was durch die bessere Optimierung oft (mehr als) wett gemacht wird.
Wo ich da eher Bloat sehe, ist beim Compilieren. Das dauert bei sehr
template-lastigem Code gerne mal ewig, was die Entwicklung verlangsamt,
weil ich ständig auf den Compiler warten muss. Dazu kommen die extrem
gesprächigen und sehr viel schwieriger zu lesenden
Compiler-Fehlermeldungen.
Templates sind ein cooles Feature, aber ich will nicht mein ganzes
Programm als großes Template schreiben.
Rolf M. schrieb:> Wilhelm M. schrieb:>> Immer will man das. Es sei denn, der DT der Elemente ermöglichen ein>> Sentinel. Ansonsten wäre man ja wieder bei dem C Scheiß.>> Wenn die Funktion std::arrays mit unterschiedlichen Größen verarbeiten> können soll, bleibt mir wie DPA schon schreibt neben einem Template nur> die Möglichkeit, sowas wie einen Iterator zu nehmen.
Auch dann wird die Funktion i.A. ein Template über den Elementtyp sein.
> Wenn ich das tue,> bin ich aber tatsächlich da angelangt, wo C ist, denn das ist dann im> Prinzip das gleiche wie der Zeiger auf den Anfang des Arrays.
Nein. Ein Zeiger ist zwar ein Iterator, aber nicht jeder Iterator ist
ein Zeiger.
Der Aufrufer hat oft die Aufgabe, das halb-offene Intervall anzupassen.
Ansonsten unterstützen Iteratoren (nicht alle, je nach Typ) den op-(),
und damit kann man die Elementanzahl ermitteln. Dies ist aber in den
meisten Fällen nicht wichtig, wenn man Iteratoren nimmt, da die ja das
Nutzintervall bestimmen.
Andernfalls nimmt man std::span<> o.ä. (std::stringview).
> An die> Größe komme ich wieder nicht heran, sofern sie nicht separat übergeben> wird.
Quatsch, s.o.
>> Und der angenommene Code-Bloat ist ein viel genanntes Pseudo-Argument,>> was durch die bessere Optimierung oft (mehr als) wett gemacht wird.>> Wo ich da eher Bloat sehe, ist beim Compilieren. Das dauert bei sehr> template-lastigem Code gerne mal ewig, was die Entwicklung verlangsamt,> weil ich ständig auf den Compiler warten muss. Dazu kommen die extrem> gesprächigen und sehr viel schwieriger zu lesenden> Compiler-Fehlermeldungen.
Da hat sich (z.B. auch bei clang) extrem viel getan. Es ist seit einigen
Jahren längst nicht mehr so wie anno-dazumals.
Und lieber Fehlermeldungen des Compilers lesen als langes
Laufzeit-Debugging.
> Templates sind ein cooles Feature, aber ich will nicht mein ganzes> Programm als großes Template schreiben.
Warum nicht?
Für die meisten µC Sachen mache ich das und es funktioniert absolut
hervorragend.
Wilhelm M. schrieb:> inline ist ein oft missverstandenes Schlüsselwort: es bedeutet, dass> eine Verletzung der ODR durch den Linker "repariert" wird (einfach> gesprochen).
Nette Idee static constexpr im class scope für ODR zu "missbrauchen".
:-)
Das funktioniert aber auch ohne inline und ohne CTAD.
Statt
Mikro 7. schrieb:> Nette Idee static constexpr im class scope für ODR zu "missbrauchen".> :-)
static constexpr ist implizit inline: sorry, Fehler von mir ;-)
Ich meinte C++11: Durch den "struct Config" scope wird ODR sicher
gestellt und man kann quasi im Header initialisieren (im Source
braucht es dann nur noch eine Dummy Definition).
In C++17 (inline) brauchst du den "struct Config" scope doch gar nicht,
oder wofür soll der dort gut sein?