Nicht Kompilieren bei einem bestimmten Wert in einem const array

#7470033
Lesenswert?

Hi,

ich möchte folgendes realisieren:

1
#include <stdio.h>
2

3
const int test[][4] = { {1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4} };
4

5
static_assert(test[0][1] < 5, "Fehler");
6
static_assert(test[1][1] < 5, "Fehler");
7
static_assert(test[2][1] < 5, "Fehler");
8
static_assert(test[3][1] < 5, "Fehler");
9

10

11
int main()
12
{
13
    printf("%d", test[0][1]);
14

15
    return 0;
16
}

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...

#7470056
Lesenswert?

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...

#7470073
Lesenswert?

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

#7470212
Lesenswert?

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.

#7471009
Lesenswert?

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
const int test[][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
int main()
13
{
14
    assert( test[0][1] < 5 );  
15
    printf("%d", test[0][1]);
16
    return 0;
17
}

Edit: Oder soll es doch C++ sein?

#7471075
Lesenswert?

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.

#7471091
Lesenswert?

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.

#7471155
Lesenswert?

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 {         \
16
  extern void assert_by_nonexisting_function__##msg();     \
17
  if( !(cond)) {assert_by_nonexisting_function__##msg();}  \
18
} while(0)
19

20
#include <stdio.h>
21

22
const int test[][4] = {{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4} };
23
const int testvar = 256;
24

25
int main()
26
{
27
  // error: undefined reference to `assert_by_nonexisting_function_test_parameter_muss_kleiner_5_sein'
28
  STATIC_ASSERT_ON_LINKTIME(test[0][1] < 5, test_parameter_muss_kleiner_5_sein);
29
  STATIC_ASSERT_ON_LINKTIME((testvar % 4 == 0), testvar_muss_vielfaches_von_4_sein);
30

31
    printf("%d", test[0][1]);
32
    return 0;
33
}
#7471217
Lesenswert?

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

1
#include <stdio.h>
2

3
#define Y0X0 1
4
#define Y0X1 2
5
#define Y0X2 3
6
#define Y0X3 4
7

8
#define Y1X0 1
9
#define Y1X1 2
10
#define Y1X2 3
11
#define Y1X3 4
12

13
#define MAX_XY 3
14

15
const int test[][4] = {
16
     {Y0X0,Y0X1,Y0X2,Y0X3},
17
     {Y1X0,Y1X1,Y1X2,Y1X3}
18
};
19
    
20
#if ( (Y0X0>MAX_XY) || (Y0X1>MAX_XY) || (Y0X2>MAX_XY) || (Y0X3>MAX_XY) ) 
21
    #error Fehler, Y0X? zu gross
22
#endif
23
 
24
#if ( (Y1X0>MAX_XY) || (Y1X1>MAX_XY) || (Y1X2>MAX_XY) || (Y1X3>MAX_XY) )
25
    #error Fehler, Y1X? zu gross
26
#endif
27
  
28
int main()
29
{
30
    printf("%d", test[0][1]);
31
    return 0;
32
}

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.

#7471236
Lesenswert?

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.

#7471260
Lesenswert?

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.

#7471278
Lesenswert?

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?

#7471499
Lesenswert?

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 {         \
21
  extern void assert_by_nonexisting_function__##msg();     \
22
  if( !(cond)) {assert_by_nonexisting_function__##msg();}  \
23
} while(0)
24

25
#include <stdio.h>
26

27
const int test[][4] = {{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4} };
28

29

30
#define ARRAY_COUNT(a) (sizeof(a)/(sizeof(a[0])))
31

32
int main()
33
{
34
  // Assertions: Parameter 2 jedes Datendsatzes muss kleiner 5 sein
35
  // Schleife wird durch -O1 vollständig raus optimiert.
36
  for(size_t i=0; i<ARRAY_COUNT(test); i++) {
37
    STATIC_ASSERT_ON_LINKTIME(test[i][1] < 5, test_parameter_muss_kleiner_5_sein);    
38
  }
39

40
  printf("%d", test[0][1]);
41
  return 0;
42
}

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" 🙂

#7471554
Lesenswert?

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:

1
constexpr int test[4][4] = { {1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4} };
2

3
static_assert([]{
4
    for(const auto& v : test) {
5
        for(const auto& e : v) {
6
            if (e > 4) return false;
7
        }
8
    }
9
    return true;
10
}(), "wrong value in array test");

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.

#7471571
Lesenswert?

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.

1
const int test[][4] = {{1,2,3,4},
2
{1,2,3,4},
3
{1,2,3,4},
4
{1,2,3,4},
5
{1,2,3,4},
6
{1,2,3,4},
7
{1,2,3,4},
8
{1,2,3,4},
9
{1,2,3,4},
10
{1,2,3,4},
11
{1,2,3,4},
12
{1,2,3,4},
13
{1,2,3,4},
14
{1,2,3,4},
15
{1,2,3,4},
16
{1,2,3,4},
17
{1,2,3,4},
18
{1,2,3,4} 
19
};
#7471611
Lesenswert?

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.

#7471614
Lesenswert?

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.

#7471627
Lesenswert?

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:

1
  #pragma GCC unroll 1000
2
  for(size_t i=0; i<ARRAY_COUNT(test); i++) {
3
    STATIC_ASSERT_ON_LINKTIME(test[i][1] < 5, test_parameter_muss_kleiner_5_sein);    
4
  }

Ich höhre jetzt aber auf, will Euch nicht zur Weisglut bringen.

Schönen Abend!

#7471643
Lesenswert?

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
>   #pragma GCC unroll 1000
2
>   for(size_t i=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 ;-)

#7471929
Lesenswert?

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!

Persönliche Seite #7471948
Lesenswert?

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
const int a[] = { 1, 3 };
2

3
__attribute__((__error__("some message")))
4
void fun_error (void);
5

6
__attribute__((__optimize__("Os")))
7
void test_a1 (void)
8
{
9
    if (a[1] > 2)
10
        fun_error();
11
}
12

13
int main ()
14
{
15
    return 0;
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.

#7471950
Lesenswert?

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 {         \
18
  extern void assert_by_nonexisting_function__##msg();     \
19
  if( !(cond)) {assert_by_nonexisting_function__##msg();}  \
20
} while(0)
21

22
#define ARRAY_COUNT(a) (sizeof(a)/(sizeof(a[0])))
23

24
const int test[][4] = {{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,222,3,4},{1,2,3,4},
25
{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4}};
26

27
/* Const Assertions Wrapper
28
  Alle STATIC_ASSERT_ON_LINKTIME() Aufrufe sollten in dieser Funktion gekapselt werden.
29
  Durch den Optimierungslevel der Funktion ist sichergestellt, dass ohne globale
30
  Optimnierung keine ungewollten Assertions auftreten.
31

32
  @note Limitationen:
33
  - nur mit gcc nutzbar
34
  - for schleifen sind auf 65534 iterationen begrenzt.
35
*/
36
__attribute__ ((optimize ("-O3"))) void static_assert_checks()
37
{
38
  STATIC_ASSERT_ON_LINKTIME(ARRAY_COUNT(test) < 65535, nachfolgende_assertion_geht_nur_fuer_arrays_kleiner_65535);    
39
  #pragma GCC unroll 65534
40
  // Assertions: Parameter 2 jedes Datendsatzes muss kleiner 5 sein
41
  for(size_t i=0; i<ARRAY_COUNT(test); i++) {
42
    STATIC_ASSERT_ON_LINKTIME(test[i][1] < 5, test_parameter_muss_kleiner_5_sein);
43
  }
44
}  
45

46
int main()
47
{
48
  static_assert_checks();
49
  printf("%d", test[0][1]);
50
  return 0;
51
}
#7471974
Lesenswert?

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
> const int test[][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
> int main()
13
> {
14
>     assert( test[0][1] < 5 );
15
>     printf("%d", test[0][1]);
16
>     return 0;
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; }

#7472007
Lesenswert?

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.

1
#include <stdio.h>
2

3

4
/*! Erzwingt unter gcc eine Fehlermeldung */
5
__attribute__((__error__("STATIC_ASSERT_CONST() Error"))) void static_assert_const_error(void);
6

7
/*! Assertions for const
8
  @param cond Bedingung wird auf true geprüft. Bei false wird ein Error erzeugt
9
  @param msg  Fehlermeldung 
10

11
  Vor C23 kennt C kein constexpr. static_assert() funktioniert bei "const" Variablen nicht.
12
  Um trotzdem Bedingungen von const Variablen nutzen zu können, kann man dieses Makro nutzen.
13
  Bei cond==false erzeugt gcc eine Fehlermeldung mittels call auf static_assert_const_error().
14

15
  @note 
16
  * Muss innerhalb einer Funktion aufgerufen werden.
17
  * Geht nur mit gcc
18
  * Aufrufe sollten in einer Funktion mit aktivierter Optimierung gekapselt werden. 
19
  * Siehe static_assert_checks
20
*/
21
#define STATIC_ASSERT_CONST(cond, msg) do {if( !(cond)) {static_assert_const_error();}} while(0)
22

23
#define ARRAY_COUNT(a) (sizeof(a)/(sizeof(a[0])))
24

25
const int test[][4] = {{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,1,3,4},{1,2,3,4},
26
{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4}};
27

28

29
/* Const Assertions Wrapper
30
  Alle STATIC_ASSERT_CONST() Aufrufe sollten in dieser Funktion gekapselt werden.
31
  Durch den Optimierungslevel der Funktion ist sichergestellt, dass ohne globale
32
  Optimnierung keine ungewollten Assertions auftreten.
33

34
  @note Limitationen:
35
  - nur mit gcc nutzbar
36
  - for schleifen sind auf 65534 iterationen begrenzt.
37
*/
38
__attribute__ ((optimize ("-Os"))) void static_assert_checks()
39
{
40
  STATIC_ASSERT_CONST(ARRAY_COUNT(test) < 65535, "Nachfolgende assertion geht nur fuer arrays kleiner 65535");
41
  #pragma GCC unroll 65534
42
  // Assertions: Parameter 2 jedes Datendsatzes muss kleiner 5 sein
43
  for(size_t i=0; i<ARRAY_COUNT(test); i++) {
44
    STATIC_ASSERT_CONST(test[i][1] < 5, "Test Parameter muss kleiner 5 sein");
45
  }
46
}  
47

48
int main()
49
{
50
  static_assert_checks();
51
  printf("%d", test[0][1]);
52
  return 0;
53
}
#7472090
Lesenswert?

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.

#7536087
Lesenswert?

... nicht schön, aber so geht's:

1
#define TABLE(A, B,C, D, E, F, G, H, I, J, K, L, M, N, O, P) \
2
    static_assert((A) < 5, "Fehler"); \
3
    static_assert((B) < 5, "Fehler"); \
4
    /* ... */                                    \
5
    const int test[][4] = {{(A), (B), (C), (D)}, \
6
                           {(E), (F), (G), (H)}, \
7
                           {(I), (J), (K), (L)}, \
8
                           {(M), (N), (O), (P)}}; 
9

10
TABLE(5, 2, 3, 4, \
11
      1, 2, 3, 4, \
12
      1, 2, 3, 4, \
13
      1, 2, 3, 4)
14

15
int main()
16
{
17
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren