"Christmas - the time to fix the computers of your loved ones" « Lord Wyrm

c++: preprocessor directives

wergor 27.07.2017 - 16:26 2822 10
Posts

wergor

connoisseur de mimi
Avatar
Registered: Jul 2005
Location: graz
Posts: 4027
im folgenden beispiel will ich einen µC- abhängigen check zur compilezeit definieren:
Code: CPP
	//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
Registered: Jan 2003
Location: Wien
Posts: 5774
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?! :D
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
Avatar
Registered: Jul 2005
Location: graz
Posts: 4027
die defines kommen nicht von mir. welche macros defined sind hängt ab welches board zur compilezeit in der Arduino-IDE ausgewählt ist.
Code: CPP
#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
Registered: Jan 2003
Location: Wien
Posts: 5774
Aso aso, na gut, is egal... Integer bleibt Integer

Code: C++
// 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
Avatar
Registered: Jul 2005
Location: graz
Posts: 4027
Zitat aus einem Post von Vinci
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 :bash:
Bearbeitet von wergor am 28.07.2017, 10:10 (typo)

Vinci

hatin' on summer
Registered: Jan 2003
Location: Wien
Posts: 5774
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
Avatar
Registered: Jul 2005
Location: graz
Posts: 4027
ich glaube nicht dass ich das mit einer constexpr optimieren kann, der wert von pin_ ist erst zur laufzeit bekannt.

Paxi

Overclocking Team Member
Avatar
Registered: Oct 2009
Location: Wien
Posts: 384
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
Registered: Jan 2003
Location: Wien
Posts: 5774
Zitat aus einem Post von wergor
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)

Code: C++
  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
Avatar
Registered: Aug 2003
Location: ~
Posts: 1624
Äh is ein "Preprogrammer" was anderes als ein "Preprocessor" im Zusammenhang mit µCs :D?

wergor

connoisseur de mimi
Avatar
Registered: Jul 2005
Location: graz
Posts: 4027
aaaaaah natürlich preprocessor... sorry, mein hirn...
Kontakt | Unser Forum | Über overclockers.at | Impressum | Datenschutz