Forum: Compiler & IDEs assert in Array-Initialisierung


von Ben (Gast)


Lesenswert?

Hallo,

folgendes kompilierbare Minimalbeispiel:
1
#define CHECK_VALUE(val)      (val)
2
3
int myArray[] =
4
{
5
    CHECK_VALUE(8),
6
    CHECK_VALUE(9),
7
    CHECK_VALUE(10)
8
};
9
10
11
int main(void)
12
{
13
14
}

Wie man sieht macht CHECK_VALUE momentan noch garnichts, außer den Wert 
wieder "zurückzugeben".

Ich wollte nun das Makro dementsprechend erweitern dass
a) weiterhin der Wert zurückgegeben wird
b) mittels assert ein Compile-Zeit Check durchgeführt wird, ob der Wert 
im Range ist (Bsp: <= 9)


Das krieg ich nicht hin, also hab ich erstmal geschaut ob ein 
_Static_assert in einer Arrayinitialisierung überhaupt klappt:
1
#define CHECK_VALUE(val)      (val)
2
3
#define MY_ASSERT(val)  _Static_assert (val <= 9, "assert1");
4
5
int myArray[] =
6
{
7
    CHECK_VALUE(8),
8
    CHECK_VALUE(9),
9
    CHECK_VALUE(10)
10
    MY_ASSERT(10)
11
12
};
13
14
15
int main(void)
16
{
17
18
}
Hier meckert der Compiler:
1
../main.c:46:3: error: expected '}' before '_Static_assert'

Der Aufruf erfolgt mittels -std=c11.
Ist das was ich vorhabe überhaupt möglich?
Kann man _Static_assert wirklich nur in Funktionen und "freestanding" 
verwenden?
Gibt es andere Lösungen?

Viele Grüße

von Oliver (Gast)


Lesenswert?

Ein define macht immer grundsätzlich nur eine Textersetzung. Der 
Compiler bekommt das also gar nicht zusehen, der sieht immer nur den 
schon aufgelösten Code.

Das sollte deine beiden Fragen beantworten. (die  nach dem 
static_define, und die die, warum es jetzt nicht geht).

Oliver

von Ben (Gast)


Lesenswert?

Oliver schrieb:
> Ein define macht immer grundsätzlich nur eine Textersetzung....

Das ist mir schon klar.

Ich kann auch schreiben:
1
int myArray[] =
2
{
3
    CHECK_VALUE(8),
4
    CHECK_VALUE(9),
5
    CHECK_VALUE(10)
6
    _Static_assert (10 <= 9, "assert1");
7
};
was natürlich das selbe ist.

Das klappt aber, natürlich mit der gleichen Fehlermeldung, ebenfalls 
nicht.

Weswegen ich auch hier nachfrage ob mein Unterfangen nicht durchführbar 
ist, d.h. ich _Static_assert einfach falsch verwende.

von Karl H. (kbuchegg)


Lesenswert?

Ben schrieb:

> Weswegen ich auch hier nachfrage ob mein Unterfangen nicht durchführbar
> ist, d.h. ich _Static_assert einfach falsch verwende.

Ich denke das kann hier nicht wirklich funktionieren, weil an dieser 
Stelle ja nur Initialisierungs-Ausdrücke möglich sind. Ein 
_Static_assert ist aber kein Ausdruck, der einen Wert liefern würde. 
Daher wäre das logisch gesehen an dieser Stelle schon mal nicht möglich.

Hmm. Ich denke, da musst du zurück zur alten Technik der Präprozessor 
#if Anweisungen, so die aufgrund der Datentypen einsetzbar sind.

von Ben (Gast)


Lesenswert?

Karl Heinz schrieb:
> _Static_assert ist aber kein Ausdruck, der einen Wert liefern würde.
> Daher wäre das logisch gesehen an dieser Stelle schon mal nicht möglich.

Danke KH.

Ich war irgendwie der Meinung, ein _Static_assert würde im OK-Fall zu 
"garnix" expandieren, so dass es überall stehen kann.

Die Lösung mit #if ... #error hatte ich schonmal, die funktioniert 
grundsätzlich.

Allerdings stelle ich gerade auf ein Array um, so dass ich eine Variable 
Anzahl an Einträgen verwalten kann. Dementsprechend wüsste ich nicht, 
wie eine Lösung mit #if... hier aussehen würde. Ein #if ist ja erstmal 
ein statisches Konstrukt. Ändert sich die Anzahl an Einträgen im Array 
müsste der #if Block jedesmal angepasst werden.
Diesen Aufwand wollte ich durch mein CHECK_VALUE Makro direkt bei der 
Initialisierung erschlagen.

Gibt es da vielleicht noch einen anderen Weg?

von B. S. (bestucki)


Lesenswert?

Solche Dinge erledige ich normalerweise mit Funktionen, diese wird 
einmalig bei Programmstart aufgerufen:
1
assert(CheckMyArray());

Wird nun NDEBUG definiert, fällt der Funktionsaufruf automatisch weg. In 
der finalen Version hinterlässt diese Variante also keine Spuren.

von Karl H. (kbuchegg)


Lesenswert?

Ich orientiere mich jetzt hierran

http://books.google.at/books?id=MAAuAgAAQBAJ&pg=PA762&lpg=PA762&dq=_Static_assert&source=bl&ots=Y-ma7f-uC3&sig=lLe2lPQ6c9OZPHGHWIRc8sfbvGA&hl=de&sa=X&ei=W9M7VInsEYqS7AbP6YCoCQ&ved=0CCkQ6AEwAjgK#v=onepage&q=_Static_assert&f=false
1
In Terms of syntax, _Static_assert is treated as a declaration statement.

Daher kann das an dieser Stelle nicht funktionieren. Du kannst ja auch 
nicht
1
int ij[5] = { 1, 2, 3, extern int k };

schreiben.

> Ich war irgendwie der Meinung, ein _Static_assert würde
> im OK-Fall zu "garnix" expandieren, so dass es überall stehen kann.

Das spielt in dem Fall keine Rolle mehr. Die Frage ist, wie 
_Static_assert in die Syntax eingebaut wurde.

von Ben (Gast)


Lesenswert?

Soweit OK.

Leider bringt mich das nicht wirklich weiter. Was ich bisher weiß ist, 
dass es mit _Static_assert wohl nicht klappen wird. Wenn es eine andere 
Lösung gibt wär ich aber auch zufrieden.

Im Prinzip will ich ja sowas hier haben:
1
#define CHECK_VALUE(val)    #if (val >= ERROR_VAL)   \
2
                                #error Out of Range! \
3
                            #else                    \
4
                                (val)                \
5
                            #endif
6
7
int myArray[] =
8
{
9
    CHECK_VALUE(8),
10
    CHECK_VALUE(9),
11
    CHECK_VALUE(10)
12
};

Das der Code nicht kompiliert ist mir klar.
Aber er sollte deutlich machen was ich will. Mit asserts kann ich diese 
Funktionalität also nicht erreichen.
Das Beispiel oben klappt auch nicht.

Hat jemand vielleicht noch einen Vorschlag?

Viele Grüße!

von PittyJ (Gast)


Lesenswert?

Ich verstehe den Sinn nicht so ganz.
Im Normalfall weiß doch der Programmierer am besten, welche Konstanten 
er in den Code hinein kompiliert. Warum sollte der Kompiler die 
Konstanten noch einmal abprüfen?

Benutzereingaben werden geprüft. Aber das macht ein Programm ja erst zur 
Laufzeit und nicht schon zur Kompilierzeit.

von Ben (Gast)


Lesenswert?

Auch wenn der Sinn nicht Thema dieses Threades werden soll:

Es handelt sich um Konfigurationseinträge, die auf Plausibilität geprüft 
werden sollen. Ist kein Muss, denn wie du schon sagst sollte der 
Programmierer wissen was erlaubt ist und was nicht. Schöner fände ichs 
aber wenns nochmal überprüft wird. Allerdings nur, wenn dies zur 
Compilezeit möglich ist, denn Ressourcen (ROM oder RAM) will ich dafür 
nicht aufwenden.

Beispiel: in sämtlichen UART-Beispielen die man hier findet prüft der 
Präprozessor die Baudrate auch nochmal (z.B. ob der Fehler größer 2% 
ist).
Dadurch wird verhindert, dass Code kompiliert wird der anschließend 
wahrscheinlich nicht zufriedenstellend läuft. Findest du das auch 
unsinnig? Ist im Prinzip das gleiche wie ich vorhabe.

von Daniel A. (daniel-a)


Lesenswert?

Ich kenne _Static_assert nicht, aber man kann ja ausprobieren ;)
Funktioniert fileicht sowas, den komma operator nutzen?
1
#define CHECK_VALUE(val)  (_Static_assert (val <= 9, "assert1"), val)
2
3
int myArray[] =
4
{
5
    CHECK_VALUE(8),
6
    CHECK_VALUE(9),
7
    CHECK_VALUE(10)
8
};

Oder eine Makroliste (getestet)?
1
// header file
2
#ifndef STR
3
#define STR(x) #x
4
#endif
5
#ifndef EVAL
6
#define EVAL(x,y) x(y)
7
#endif
8
#define SEMIKOLON ;
9
#define KOMMA ,
10
#define BUILD_ARRAY(x) x
11
#define CHECK_ARRAY(x) _Static_assert(CHECK(x), "Assert failed in " __FILE__ ":" EVAL(STR,__LINE__) " for value " #x ": expression " EVAL(STR,CHECK(x)) " is false!")
12
13
// c file
14
15
#define CHECK(x) x <= 9
16
#define LISTE(E,D) \
17
  E(8) D\
18
  E(9) D\
19
  E(10)
20
21
LISTE(CHECK_ARRAY, SEMIKOLON);
22
23
int myArray[] = {
24
  LISTE(BUILD_ARRAY, KOMMA)
25
};
26
27
#undef LISTE
28
#undef CHECK

: Bearbeitet durch User
von B. S. (bestucki)


Lesenswert?

Daniel A. schrieb:
> #define CHECK_VALUE(val)  (_Static_assert (val <= 9, "assert1"), val)

Das funktioniert nicht. Der Compiler meckert schon, wenn man das 
static_assert nur umklammert:
1
(static_assert(1, "test")); // [Error] expected expression before '_Static_assert'


Scheinbar funktioniert folgendes:
1
static const char Help__ = ' ';
2
#define CHECK(val) (((val) < 10) ? Help__ : val)
Ist val kleiner 10, wirft der Compiler folgende Fehlermeldung:
1
[Error] initializer element is not constant
Etwas unschön, da die Fehlermeldung nicht direkt etwas mit der Prüfung 
des Wertes zu tun hat, aber immerhin eine Fehlermeldung.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.