c++: preprocessor directives
wergor 27.07.2017 - 16:26 3068 10
wergor
connoisseur de mimi
|
im folgenden beispiel will ich einen µC- abhängigen check zur compilezeit definieren: //check if the input pin is an analog pin. return false if it is not.
#ifdef __SAM3X8E__
if ((pin_ < A0) || (pin_ > A11)) //Arduino Due: 12 ADC inputs
#elif defined __AVR_ATmega2560__
if ((pin_ < A0) || (pin_ > A15)) //Arduino Mega: 16 ADC inputs
#elif defined __ESP8266_ESP8266__
if (pin_ != A0) //ESP8266: 1 ADC input
#else
if ((pin_ < A0) || (pin_ > A5))
#endif
return false;
return true;
die letzte directive (#else) funktioniert nicht wie erwartet, der code darin wird immer kompiliert. weis jemand woran das liegen könnte?
Bearbeitet von wergor am 28.07.2017, 12:56
|
Vinci
hatin' on summer
|
Hm, ein gacher "cout" Test haut bei mir ohne Probleme hin (gcc 7.1) Wie sieht denn das #define von A0 bis 15 aus? Und abgesehn davon... warum zur Hölle machst du das?! Wenn du einen C++11 fähigen Compiler hast, dann schmeiß jedes einzelne #define aus dem Code raus. So gut wie alles lässt sich viel schöner durch constexpr und Phantom Types oder ähnlichem abbilden.
|
wergor
connoisseur de mimi
|
die defines kommen nicht von mir. welche macros defined sind hängt ab welches board zur compilezeit in der Arduino-IDE ausgewählt ist. #define PIN_A0 (54)
#define PIN_A1 (55)
//[...]
#define PIN_A15 (69)
static const uint8_t A0 = PIN_A0;
//[...]
static const uint8_t A15 = PIN_A15;
|
Vinci
hatin' on summer
|
Aso aso, na gut, is egal... Integer bleibt Integer // some enum class for the selected board
enum class Board
{
SAM3X8E,
ATmega2560,
ESP8266
};
// compile time check if board has ADC pin
constexpr bool hasPin(Board board, int pin)
{
switch (board)
{
case Board::SAM3X8E:
if (pin < 54 || pin > 65) // use library defines here
return false;
break;
case Board::ATmega2560:
if (pin < 54 || pin > 69)
return false;
break;
case Board::ESP8266:
if (pin != 0)
return false;
break;
}
return true;
}
int main()
{
if (hasPin(Board::SAM3X8E, 42))
std::cout << "pin 42 da\n";
else
std::cout << "pin 42 ned da\n";
if (hasPin(Board::SAM3X8E, 55))
std::cout << "pin 55 da\n";
else
std::cout << "pin 55 ned da\n";
}
Das ganze geht natürlich noch wesentlich generischer und schöner... Etwa in dem man anfängt komplette Konfigurationen für jedes Board abzulegen und compile-time Checks einfügt, die vornweg gleich einmal verhindern, das jemand Pin XY als ADC Pin verwenden will. Dazu müsste man aber anfangen die Bibliotheken für das Klump selbst zu schreiben. Da das 3x komplett verschiedene Architekturen sind, eine vermutlich recht zache Aufgabe.
|
wergor
connoisseur de mimi
|
recht zache Aufgabe korrekt. hm, das problem scheint doch nur beim __ESP8266_ESP8266__ macro aufzutreten. der compiler behandelt es als wäre es nicht defined. sehr seltsam. edit: das korrekte makro für den ESP8266 ist ARDUINO_ESP8266_NODEMCU
Bearbeitet von wergor am 28.07.2017, 10:10 (typo)
|
Vinci
hatin' on summer
|
Du solltest trotzdem zur constexpr greifen, die dir einen echten compile time check liefert. Es ist zwar "nur ein if", aber trotzdem mindestens 2 unnötige Instructions wenns der Compiler nicht wegoptimiert.
|
wergor
connoisseur de mimi
|
ich glaube nicht dass ich das mit einer constexpr optimieren kann, der wert von pin_ ist erst zur laufzeit bekannt.
|
Paxi
Overclocking Team Member
|
Man müsste sehen was der Compiler genau daraus macht. Aber im Grunde sollte es funktionieren, da der Wert Board::SAM3X8E und 42 bereits zur Compile Zeit bekannt ist und von keiner Laufzeitevaluierung abhängig ist. Ein klassische Beispiel für eine constexpr Funktion die Input bekommt ist zb.: die "factorial" Funktion, die ebenso einen Integer bekommt und sich darin sogar selbst rekursiv aufruft. Siehe hier: http://blog.quasardb.net/demystifying-constexpr/
|
Vinci
hatin' on summer
|
ich glaube nicht dass ich das mit einer constexpr optimieren kann, der wert von pin_ ist erst zur laufzeit bekannt. Die constexpr verschwindet garantiert. Das lässt sich leicht testen, indem man aus if in der main ein constexpr if macht. (C++17 vorrausgesetzt) if constexpr (hasPin(Board::SAM3X8E, 42))
std::cout << "pin 42 da\n";
else
std::cout << "pin 42 ned da\n";
etc. Sollte pin_ halt wirklich erst zur Laufzeit bekannt sein hilft das alles nix. Ich wetter aber nachwievor gegen die #defines und selbst wenn man den "Arduino legacy Code" weiternutzen will, dann würd ich dazwischen eine Funktion klemmen. Der Compiler optimiert das eh weg, aber alles is besser als an hunderten Stellen im Code ein #ifdef zu haben.
|
-=Willi=-
The Emperor protects
|
Äh is ein "Preprogrammer" was anderes als ein "Preprocessor" im Zusammenhang mit µCs ?
|
wergor
connoisseur de mimi
|
aaaaaah natürlich preprocessor... sorry, mein hirn...
|