Die static_assert funktionieren so aber nicht.
Wie bekomme ich das hin das wenn ein bestimmtes Array ein ungültigen
Wert besitzt (in dem Fall der Wert ist größer als 5) nicht kompiliert
wird.
Natürlich geht es nicht genau um diesen Code sondern um das Prinzip.
Hintergrund ist eher das eine gesharede Lib gibt, die jeder Anpassen
kann, es ist aber wichtig, das wenn jemand da was hinzufügt was ungültig
ist, es gar nicht erst kompiliert. Viele Dinge sind leider historisch
gewachsen und soetwas wäre die leichteste Lösung...
Ah Mist, vergessen noch zu Erwähnen welche Umgebung es geht usw.
Also es ist leider nur reines C. Der GCC Compiler vom Microchip Studio.
Da ist ja leider C++ bzw constexpr nicht möglich.
So wie Wilhelm schreibt.
Als neuer Nutzer kann ich leider nur iwie alle 30 Minuten was
schreiben...
Eske schrieb:> Da ist ja leider C++ bzw constexpr nicht möglich.
constexpr ist mit einem C23-fähigem Compiler zwar möglich, aber im
static_assert funktioniert das dann trotzdem nicht.
Liest man die Kommentare zu constexpr in C, dann dürfte das so gewollt
sein. C soll halt weiterhin ein besserer Macro-Assembler bleiben, bei
dem der Programmier jegliche Freiheiten hat, beliebige Fehler zu machen.
Oliver
Eske schrieb:> Hintergrund ist eher das eine gesharede Lib gibt, die jeder Anpassen> kann, es ist aber wichtig, das wenn jemand da was hinzufügt was ungültig> ist, es gar nicht erst kompiliert.
Notfalls halt einen test dafür schreiben. Kleines Programm was die
Library dazulinkt und die Einträge prüft.
Muss ja kein vollausgewachsenes CI/CD-Setup werden, kriegt man auch so
in ein Makefile gebastelt, dass der Buildprozess abbricht.
Eske schrieb:> Die static_assert funktionieren so aber nicht.
Weil man mit const in C keine echten Compilezeit-Konstanten definiert,
sondern nur Variablen, die man nicht ändern kann. Und auf Variablen kann
man kein static_assert anwenden.
Eske schrieb:> Ah Mist, vergessen noch zu Erwähnen welche Umgebung es geht usw.> Also es ist leider nur reines C. Der GCC Compiler vom Microchip Studio.>> Da ist ja leider C++ bzw constexpr nicht möglich.
Ist da kein g++ dabei?
> Als neuer Nutzer kann ich leider nur iwie alle 30 Minuten was> schreiben...
Da darfst du dich vermutlich beim Haustroll bedanken, der sonst das
Forum mit endlosen Kopien seiner Ergüsse fluten würde.
Veit D. schrieb:> so sollte das im Grunde funktionieren. Allerdings werden Fehler nur auf> der stderr ausgeben. Einen Compilerabbruch wie bei C++ gibt es nicht.
Ja, die Prüfung wird dann erst zur Laufzeit durchgeführt, mit allen
Nachteilen, die das mit sich bringt. Auf einem µC hat man eventuell auch
gar nicht, wo stderr hingeht.
Veit D. schrieb:> Allerdings werden Fehler nur auf> der stderr ausgeben. Einen Compilerabbruch wie bei C++ gibt es nicht.
Und deswegen trifft es nicht die Anforderungen des TO (s.o.).
Oliver S. schrieb:> constexpr ist mit einem C23-fähigem Compiler zwar möglich, aber im> static_assert funktioniert das dann trotzdem nicht.
So wie ich das verstehe, geht es nur mit skalaren Objekten. Und dafür
funktioniert es auch in C23. Aber trotzdem: auch wenn es für Arrays
funktionieren würde, so fehlen noch die constexpr-Funktionen, um alle
Array-Elemente prüfen zu können.
So etwas geht über eine "Linker Assertion".
"Nachteil": es geht nur wenn Compiler/Linker nicht genutzte Funktionen
herausoptimieren darf. Das macht aber in einer Library sowieso Sinn.
Getestet mit gcc 12.2.
Du müsstest mal prüfen, ob das auch beim Erzeugen einer Library
funktioniert.
1
/*! Linker Assertion
2
@param cond Bedingung wird auf true geprüft. Bei false wird ein Linker Fehler erzeugt
3
@param msg Fehlermeldung in C-Identifier Form, z.B. dies_ist_ein_Text_als_Identifier
4
5
Vor C23 kennt C kein constexpr. static_assert() funktioniert bei "const" Variablen nicht.
6
Um trotzdem Bedingungen von const Variablen nutzen zu können, kann man dieses Makro nutzen.
7
Es ruft bei cond==false eine nicht existierende Funktion auf.
8
9
@note
10
* Muss innerhalb einer Funktion aufgerufen werden.
11
* Geht nur, wenn der Compiler nicht genutzte Funktionen löschen darf. Beispiel ``gcc -O1``.
12
Ohne Optimierung wird der Fehler immer immer geworfen.
13
14
*/
15
#define STATIC_ASSERT_ON_LINKTIME(cond, msg) do { \
Oliver S. schrieb:> So langsam erscheint da wieder die ursprüngliche Schönheit der Sprache> C.
Naja, wenn man versucht, mit irgendwelchen Tricksereien was zu machen,
das die Sprache eigentlich nicht hergibt, dann wird das immer hässlich,
nicht nur in C.
Hallo,
ich denke einmal laut für alle vor mich hin. :-)
Wäre es für den TO denkbar die Lib als C++ Lib anzulegen, alles im C
Syntax zu belassen und nur diesen zu prüfenden Teil im C++ Syntax zu
schreiben? Wenn ich so weiter denke befürchte ich allerdings, dass das
restliche C Programm mit der C++ Lib nichts anfangen kann. Dann müßte
wahrscheinlich alles als C++ Programm neu angelegt werden. Schwieriger
Fall.
Andere Idee.
Wenn man die Variablen für das Arrays mit #defines anlegt, kann man das
mit dem Präprozessor abfangen. Der Gedanke wie
Muss man eben beim Schreiben höllisch aufpassen. Wenn man das schon rein
optisch sauber anlegt hat das Auge auch die Chance irgendwelche Fehler
schneller zu sehen.
Je nach Anwendung kann es auch sinnvoll sein den Anwender gar nicht das
Array anlegen zu lassen, sondern es zu generieren aus einem anderen
Format (CSV oder einfach in einer Scriptsprache definieren). Dann können
Prüfungen beim Generieren gemacht werden. Das kann man problemlos in ein
Makefile mit einbauen.
Rolf M. schrieb:> Naja, wenn man versucht, mit irgendwelchen Tricksereien was zu machen,> das die Sprache eigentlich nicht hergibt, dann wird das immer hässlich,> nicht nur in C.
Hässlich ist ein sehr schwammiges Qualitätsmerkmal. Was verstehst du
darunter?
Aus meiner Sicht ist hässlich:
* "Man versteht nicht was das Teil Macht." -->
Im Falle von STATIC_ASSERT_ON_LINKTIME ist eigentlich schon klar dass es
sich um eine static_assert handelt, die zur Link time ausgeführt wird.
* "Man verlässt sich auf ein Konstrukt, es versagt aber unerkannt." -->
passiert hier nicht. Ist Optimierung aus, bricht das ganze ab.
* "Fehlermeldungen sind nichtssagend." --> die Linkermeldung ist durch
die Verwendung eines aussagekräftigen Identifiers gegeben. Liegt aber am
Anwender.
* "Die Dokumentation ist lausig und weist einen nicht auf Sonderfälle
hin" --> Sonderfälle sind dokumentiert.
Ich finde dass Attribut hässlich passt hier nicht.
Klaus H. schrieb:> Rolf M. schrieb:>> Naja, wenn man versucht, mit irgendwelchen Tricksereien was zu machen,>> das die Sprache eigentlich nicht hergibt, dann wird das immer hässlich,>> nicht nur in C.>> Hässlich ist ein sehr schwammiges Qualitätsmerkmal. Was verstehst du> darunter?
...
> Ich finde dass Attribut hässlich passt hier nicht.
Ich finde es ebenfalls Pott-hässlich ... und vollkommen nutzlos, solange
Du nicht alle Array-Elemente prüfst.
Hatte ich ja oben schon gefragt: welche schönes Lösung schlägst Du vor?
Wilhelm M. schrieb:> Ich finde es ebenfalls Pott-hässlich ... und vollkommen nutzlos, solange> Du nicht alle Array-Elemente prüfst.> Hatte ich ja oben schon gefragt: welche schönes Lösung schlägst Du vor?
Nichts leichter als das:
1
/* Mikrocontroller.net
2
Diskussion: Nicht Kompilieren bei einem bestimmten Wert in einem const array
3
https://www.mikrocontroller.net/topic/557973#new
4
*/
5
6
/*! Linker Assertion
7
@param cond Bedingung wird auf true geprüft. Bei false wird ein Linker Fehler erzeugt
8
@param msg Fehlermeldung in C-Identifier Form, z.B. dies_ist_ein_Text_als_Identifier
9
10
Vor C23 kennt C kein constexpr. static_assert() funktioniert bei "const" Variablen nicht.
11
Um trotzdem Bedingungen von const Variablen nutzen zu können, kann man dieses Makro nutzen.
12
Es ruft bei cond==false eine nicht existierende Funktion auf.
13
14
@note
15
* Muss innerhalb einer Funktion aufgerufen werden.
16
* Geht nur, wenn der Compiler nicht genutzte Funktionen löschen darf. Beispiel ``gcc -O1``.
17
Ohne Optimierung wird der Fehler immer immer geworfen.
18
19
*/
20
#define STATIC_ASSERT_ON_LINKTIME(cond, msg) do { \
Du kannst es selber ausprobieren: https://godbolt.org/z/ahT787859
Die Schleife wird ab -O1 vollständig rausoptimiert.
Selbst C++ Templates kommen an diese Einfachheit so nicht ran.
Das kann jeder lesen der einigermaßen C kann.
Das ist in meinen Augen nicht "Pott-hässlich" sondern eher "Nice" 🙂
MaWin O. schrieb:> Das ist nur "Nice" für Leute, die nichts anderes als C kennen.
Der TO hat ein Problem in C und nicht in C++.
Ich programmiere alles Neue nur mit C++. Aber manchmal hat man halt
C-Code, der eingesetzt wird. Und das Schöne ist: das funktioniert sogar
in C++.
(Dort würde ich aber eher constexpr empfehlen.).
Klaus H. schrieb:>> Du kannst es selber ausprobieren: https://godbolt.org/z/ahT787859>> Die Schleife wird ab -O1 vollständig rausoptimiert.
Und so einen Schwachsinn, der von den Optimierungsoptionen abhängt,
willst Du uns als Lösung verkaufen?
> Selbst C++ Templates kommen an diese Einfachheit so nicht ran.
Braucht man nicht: geht ganz einfach mit Immediate-Functions:
> Das kann jeder lesen der einigermaßen C kann.
Glaube ich nicht.
Viel schlimmer ist allerdings, dass Deine Nicht-Lösung bei größeren
Arrays nicht mehr funktioniert.
Klaus H. schrieb:> Ich programmiere alles Neue nur mit C++. Aber manchmal hat man halt> C-Code, der eingesetzt wird. Und das Schöne ist: das funktioniert sogar> in C++.
Aber so einen Blödsinn würde man in C++ nicht schreiben, weil man es
nicht braucht.
> (Dort würde ich aber eher constexpr empfehlen.).
Blitzmerker: steht ganz oben.
Klaus H. schrieb:> Die Schleife wird ab -O1 vollständig rausoptimiert.
Das ist allerdings stark compiler- und linkerspezifisch. Und mit -Og
bleibt sie drin.
Rolf M. schrieb:> Klaus H. schrieb:>> Die Schleife wird ab -O1 vollständig rausoptimiert.>> Das ist allerdings stark compiler- und linkerspezifisch. Und mit -Og> bleibt sie drin.
Auch mit -O3 bleibt sie drin, wenn das Array länger ist (s.o.).
Och Leutens, warum prügelt ihr auf mich ein? Der Vorschlag kann für den
TO eine Lösung in C sein, die (wenn -O1 möglich ist) recht einfach ist.
Ich hab nie behauptet, dass ich C++ schlecht finde oder C bevorzuge…
Rolf M. schrieb:> Das ist allerdings stark compiler- und linkerspezifisch. Und mit -Og> bleibt sie drin.
Jepp, hab ich oben geschrieben.
Klaus H. schrieb:> Och Leutens, warum prügelt ihr auf mich ein? Der Vorschlag kann für den> TO eine Lösung in C sein, die (wenn -O1 möglich ist) recht einfach ist.
Nein ist er nicht, weil er einfach bei längeren Arrays nicht
funktioniert!
So geht es selbst bei -O3 nicht mehr. Daher ist das totaler Schwachsinn.
Wilhelm M. schrieb:> So geht es selbst bei -O3 nicht mehr.
Das ist ein Argument.
Ich habe diese Technik in einem Projekt genutzt,
in dem hauptsächlich einzelne Werte und kleine Arrays geprüft wurden.
Dort hat die Optimierung immer funktioniert.
> Daher ist das totaler Schwachsinn.
Nein, schwachsinnig wäre es, wenn die Assertion nicht erkannt ausfällt.
Das tut sie aber nicht. Wenn nicht optimniert werden kann,
meldet das die Assertion. Dann muss man halt andere Techniken nutzen.
Klaus H. schrieb:> Wilhelm M. schrieb:>> So geht es selbst bei -O3 nicht mehr.>> Das ist ein Argument.>> Ich habe diese Technik in einem Projekt genutzt,> in dem hauptsächlich einzelne Werte und kleine Arrays geprüft wurden.> Dort hat die Optimierung immer funktioniert.
Tja: da hast Du einen Test für Deine Nicht-Lösung, der keine
Aussagekraft besitzt.
>> Daher ist das totaler Schwachsinn.> Nein, schwachsinnig wäre es, wenn die Assertion nicht erkannt ausfällt.> Das tut sie aber nicht. Wenn nicht optimniert werden kann,> meldet das die Assertion. Dann muss man halt andere Techniken nutzen.
Nun gut, eine Nicht-Lösung, die anzeigt, dass sie keine Lösung ist, ist
nicht ganz so schlimm wie eine Nicht-Lösung, die still versagt. Es
bleibt: ein Nicht-Lösung.
Eine Ergänzung hab ich noch. Aber gilt nur für gcc (nutzt der TO).
Per "#pragma GCC unroll" zwingt man den Compiler, die nächste for
Schleife komplett aufzurollen:
Klaus H. schrieb:> Eine Ergänzung hab ich noch. Aber gilt nur für gcc (nutzt der TO).> Per "#pragma GCC unroll" zwingt man den Compiler, die nächste for> Schleife komplett aufzurollen:
Na prima, und Du bist sicher, dass Du das für den Rest der TU möchtest?
Und warum nur bist 1000? Also, es bleibt leider Mist.
- Abhängig von der Optimierung
- Abhängig vom pragma
- Abhängig von der Array-Größe
>
1
>#pragmaGCCunroll1000
2
>for(size_ti=0;i<ARRAY_COUNT(test);i++){
3
>STATIC_ASSERT_ON_LINKTIME(test[i][1]<5,
4
>test_parameter_muss_kleiner_5_sein);
5
>}
6
>
>> Ich höhre jetzt aber auf, will Euch nicht zur Weisglut bringen.
Höhre Weisglut ;-)
Klaus H. schrieb:>> Daher ist das totaler Schwachsinn.> Nein, schwachsinnig wäre es, wenn die Assertion nicht erkannt ausfällt.> Das tut sie aber nicht. Wenn nicht optimniert werden kann,> meldet das die Assertion.
Nein, es kommt beim Linken ein Fehler, der suggeriert, dass die Werte
falsch seien, also eine vollkommen irreführende Fehlermeldung.
Erstmal vielen Dank an alle! Hab auch was dazu gelernt!
Klaus H. schrieb:> So etwas geht über eine "Linker Assertion".>> "Nachteil": es geht nur wenn Compiler/Linker nicht genutzte Funktionen> herausoptimieren darf. Das macht aber in einer Library sowieso Sinn.> Getestet mit gcc 12.2.>> Du müsstest mal prüfen, ob das auch beim Erzeugen einer Library> funktioniert.
Das sieht perfekt aus und gefällt mir richtig gut! Funktioniert auch
hier Prima und wie es gewollt ist. Vielen Dank!
Eske schrieb:> Erstmal vielen Dank an alle! Hab auch was dazu gelernt!>> Klaus H. schrieb:>> So etwas geht über eine "Linker Assertion".>>>> "Nachteil": es geht nur wenn Compiler/Linker nicht genutzte Funktionen>> herausoptimieren darf. Das macht aber in einer Library sowieso Sinn.>> Getestet mit gcc 12.2.>>>> Du müsstest mal prüfen, ob das auch beim Erzeugen einer Library>> funktioniert.>> Das sieht perfekt aus und gefällt mir richtig gut! Funktioniert auch> hier Prima und wie es gewollt ist. Vielen Dank!
In gcc (und auch g++) gibt es eine ähnliche Möglichkeit per Attribut
"error" (oder wenn man will auch "warning"):
1
constinta[]={1,3};
2
3
__attribute__((__error__("some message")))
4
voidfun_error(void);
5
6
__attribute__((__optimize__("Os")))
7
voidtest_a1(void)
8
{
9
if(a[1]>2)
10
fun_error();
11
}
12
13
intmain()
14
{
15
return0;
16
}
In test_a1() wird der Test ausgeführt, und wenn gcc einen Aufruf für
fun_error erzeugen müsste, gibt der Compiler stattdessen einen Fehler
aus:
1
foo.c: In function 'test_a1':
2
foo.c:10:9: error: call to 'fun_error' declared with attribute error: some message
3
10 | fun_error();
4
| ^~~~~~~~~~~
Damit die Funktion bei erfüllter Assertion nicht getriggert wird wenn
nicht optimiert wird, hab ich Attribute "optimize" angegeben.
Auch wenn das so funktioniert ist es nicht wirklich schön...
Immerhin eine weitere Möglichkeit in der Werkzeugkiste. Vorteil
gegenüber der o.g. Methode ist, dass die Diagnostic direkt vom Compiler
kommt und nicht vom Linker.
Eine Ergänzung noch:
Damit Du auch ohne Optimierung arbeiten kannst, kann man alle Assertions
in einer Funktion zusammenfassen. Dann wird nur diese Funktionn per
attribut optimiert.
Dann entstehen auch keine irreführende Fehlermeldung, wie Rolf angemerkt
hat.
Beispiel:
1
#include<stdio.h>
2
3
/*! Linker Assertion
4
@param cond Bedingung wird auf true geprüft. Bei false wird ein Linker Fehler erzeugt
5
@param msg Fehlermeldung in C-Identifier Form, z.B. dies_ist_ein_Text_als_Identifier
6
7
Vor C23 kennt C kein constexpr. static_assert() funktioniert bei "const" Variablen nicht.
8
Um trotzdem Bedingungen von const Variablen nutzen zu können, kann man dieses Makro nutzen.
9
Es ruft bei cond==false eine nicht existierende Funktion auf.
10
11
@note
12
* Muss innerhalb einer Funktion aufgerufen werden.
13
* Geht nur, wenn der Compiler nicht genutzte Funktionen löschen darf. Beispiel ``gcc -O1``.
14
Ohne Optimierung wird der Fehler immer immer geworfen.
15
16
*/
17
#define STATIC_ASSERT_ON_LINKTIME(cond, msg) do { \
Johann L. schrieb:> In gcc (und auch g++) gibt es eine ähnliche Möglichkeit per Attribut> "error" (oder wenn man will auch "warning"):
Stimmt, daran hatte ich nicht gedacht. Ursprünglich nutzte ich diese
Linker Assertions auf einem nicht gcc.
Veit D. schrieb:> Hallo,>> so sollte das im Grunde funktionieren. Allerdings werden Fehler nur auf> der stderr ausgeben. Einen Compilerabbruch wie bei C++ gibt es nicht.>>
1
>#include<stdio.h>
2
>#include<assert.h>
3
>
4
>constinttest[][4]={
5
>{1,2,3,4},
6
>{1,2,3,4},
7
>{1,2,3,4},
8
>{1,2,3,4}
9
>};
10
>
11
>
12
>intmain()
13
>{
14
>assert(test[0][1]<5);
15
>printf("%d",test[0][1]);
16
>return0;
17
>}
18
>
>
Das lässt sich mit ein wenig inline-Assembler tatsächlich noch
hinbetrügen (ich behaupte nicht, dass das schön ist):
#include <stdio.h>
const int test[][4] = {
{1,2,3,4},
{1,2,3,4},
{1,2,3,4},
{1,2,3,4}
};
int main()
{
_asm_ __volatile__(
".if %c0 > 4\n\t"
".err \n\t"
".endif \n\t"
:: "i" (test[0][1]):
);
printf("%d", test[0][1]);
return 0;
}
Hier nochmal eine Variante über gcc attribut error.
Jetzt wird ein Call auf eine nicht vorhandene Funktion vermieden.
Das Assertion Makro Funktion habe ich umbenannt in STATIC_ASSERT_CONST.
Die Fehlermeldung kann jetzt auch als String genutzt werden, eine
Schreibweise als Identifier ist nicht mehr notwendig.
Es gibt ja auch Leute, die gehen zu Dominas und lassen sich
auspeitschen.
Und es gibt Leute, die verwenden C; Insbesondere in Kombination mit
solchen Konstrukten.
Ich vermute es gibt eine große Schnittmenge.
Na, zum Glück ist der TO ja noch Student, und er wird seinen Code ggf.
durch ein Review bringen müssen ... oder seinem Tutor zeigen müssen.
Vielleicht sollte man ja doch mal über C++ nachdenken. Hier nochmal das
C++ Ungetüm:
Beitrag "Re: Nicht Kompilieren bei einem bestimmten Wert in einem const array"
Auch in diesem Fall empfehle ich den soften Umstieg auf C++, nur um die
kleinen Goodies zu nutzen. Denn auch hier bin ich überzeugt, dass der
Code des TO fast unmodifiziert sich als C++ übersetzen lässt.
Klaus H. schrieb:> Hier nochmal eine Variante über gcc attribut error.>> Jetzt wird ein Call auf eine nicht vorhandene Funktion vermieden.> Das Assertion Makro Funktion habe ich umbenannt in STATIC_ASSERT_CONST.> Die Fehlermeldung kann jetzt auch als String genutzt werden, eine> Schreibweise als Identifier ist nicht mehr notwendig.
Statt dieses Ungetüms, was nun nach einigen Nachfragen und weiteren
Versuchen endlich hoffentlich korrekt ist, würde ich in dem Fall eine
Laufzeitassertion (!NDEBUG) vorziehen, und ggf. in den Build-Prozess
einen Laufzeittest einbauen. Allemal besser als dieser ... Mist. Oder
wie oben schon gesagt, die kleinen Goodies von C++ nutzen.