Wie kann man typeid nutzen?

Hallo,

ich treffe in letzter Zeit vermehrt auf das Problem das ich Standardfunktionen laut Buch auf dem avr nicht verwenden kann. Was macht ihr dann?

Aktuell möchte ich ein Funktionstemplate erweitern und muss dafür den Datentyp ermitteln. Dafür bräuchte ich typeid. Kennt er aber nicht. include typeinfo ist auch nicht bekannt.

Für typeid erhielt ich die Fehlermeldung “cannot use ‘typeid’ with ‘-fno-rtti’”
Das konnte ich in der platforms.local.txt mit der Option -frtti beheben.

Allerdings weiß ich nicht woher ich das Includefile typeinfo bekomme?

include <typeinfo.h>

int a = 1;

void setup(void) {
  Serial.begin(9600);
  
  if ( typeid(a) == typeid(int) ) {
    Serial.println("int");
  }
  else {
    Serial.println("kein int");
  }
}

void loop(void) {
 
}

Das Vorhaben ist zwischen Ganzzahl und Fließkomma unterscheiden zu können.

Das gehört zur C++ std Lib, die steht, wie auch die stl, leider für AVR nicht zur Verfügung.

ARM und ESP Kompiler sollten die dabei haben.
Ob man die da klauen kann?

#include <typeinfo.h>
#include

ohne h

Das Vorhaben ist zwischen Ganzzahl und Fließkomma unterscheiden zu können.

Das geht mit Templates auch zur Compilezeit:

template<typename T, typename U>   //Typen ungleich, gibt 0 zurück
struct is_same
{
  enum { value = 0 };
};

template<typename T>   //Typen gleich, gibt 1 zurück
struct is_same < T, T >
{
  enum { value = 1 };
};

void setup(void) 
{
  Serial.begin(9600);

  int a = 1;

  if (is_same<int, decltype(a)>::value || is_same<long, decltype(a)>::value)
    Serial.print("Ist ein Integer");
  else
    Serial.print("Ist kein Integer");
}

void loop(void) 
{
}

Genaugenommen müsste man per Hand auf alle Integer-Datentypen vergleichen, aber es geht um das Prinzip

In der STL ginge das natürlich einfacher:
https://en.cppreference.com/w/cpp/types/numeric_limits/is_integer

Hallo,

immer wieder die fehlende STL. :confused: Ja die ist/kann auf einem AVR Speicher fressend sein, aber wenn man genügend RAM hat und mit bedacht bestimmte STL Funktion nutzen möchte warum nicht. So mein Gedanke dazu.

Auf der Platte im ESP Pfad liegt eine typeinfo Datei.
Ich habe diese einmal hierhin kopiert. Ich weiß ehrlich gesagt nicht wo die richtig hingehört.
C:\avrToolchain\avr-gcc-9.2.0-mingw64-selfmade\lib\gcc\avr\9.2.0\include
Ich erhalte eine include expression. Sie wird nicht gefunden.

"C:\\avrToolchain\\avr-gcc-9.2.0-mingw64-selfmade/bin/avr-g++" -c -g -Os -w -std=gnu++17 -fno-exceptions -fpermissive -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -frtti -w -x c++ -E -CC -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10809 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -fno-sized-deallocation -fconcepts "-IC:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\cores\\arduino" "-IC:\\Program Files (x86)\\Arduino\\hardware\\arduino\\avr\\variants\\mega" "C:\\Users\\Worker\\AppData\\Local\\Temp\\arduino_build_255490\\sketch\\typeid_typeinfo.ino.cpp" -o nul
In file included from C:\Users\user\AppData\Local\Temp\arduino_modified_sketch_432956\typeid_typeinfo.ino:1:
c:\avrtoolchain\avr-gcc-9.2.0-mingw64-selfmade\lib\gcc\avr\9.2.0\include\typeinfo:34:10: fatal error: exception: No such file or directory
   34 | #include <exception>
      |          ^~~~~~~~~~~

Serenifly. Dein Sketch kompiliert in deiner Arduino IDE? Weil decltype benötigt laut meines Wissens auch die include typeinfo. :o

Hallo,

jetzt bin ich überrascht. Der Arduino IDE default compiler kennt decltype. Mein avr-gcc 9.2 kennt das nicht. :o

decltype ist ein C++11 Schlüsselwort und nicht Teil irgendwelcher Bibliotheken/Header

Decltype wird zur Compilezeit evaluiert. Typeinfo ist eine Laufzeit Sache. Deshalb der Kram mit RTTI (run time type information).

Die GCC Alternative ist typeof():
https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Typeof.html

Hallo,

ich erhielt immer solchen Mist. Auch beim Gegencheck mit Zaks 9.1.0 avr-gcc.

decltype.ino.elf.vBWGvk.ltrans0.ltrans.o:(.rodata+0x7): undefined reference to `vtable for __cxxabiv1::__class_type_info'
decltype.ino.elf.vBWGvk.ltrans0.ltrans.o:(.rodata+0x13): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
decltype.ino.elf.vBWGvk.ltrans0.ltrans.o:(.rodata+0x2a): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'

Dann habe ich die Compileroption -frtti rausgenommen und es kompiliert auch mit dem 9.1 und 9.2. Compiler wieder.

Ich werde mit decltype weitermachen. :slight_smile:

Ich danke dir.

Das oben würde auch ohne decltype gehen wenn man das wiederum in ein Template packt und einfach den Typ an is_same durchreicht :slight_smile:

Ich rätsel zur Zeit noch wie dein obiger Sketch überhaupt funktioniert.

Ich komme mit den beiden Templates noch nicht mit.
Das Erste hat 2 Datentypen als Parameter, aber T und U werden nicht genutzt.
Das Zweite Template hat nur einen Datentyp als Parameter, wird doppelt übergeben aber intern wiederum nicht genutzt. Und wie man mittels 0 und 1 zwischen int und long unterscheiden kann weiß ich auch nicht. Tut mir leid.

T und U sind nur dazu da um zu erkennen welche Datentypen man übergibt. T und U sind zwei verschiedene Datentypen. Wenn also die beiden übergebenen Datentypen gleich sind wird das nicht verwendet. Sondern das andere wo beide T sind

Das enum mit 0 und 1 kann man dann mit if (… ::value) auswerten um festzustellen welche Version der Compiler verwendet hat.

Wenn du es schöner machen willst dann kannst du noch den bool() Operator überladen. Dann braucht man das nicht explizit hinschreiben. So:

template<typename T, typename U>
struct is_same
{
	enum { value = 0 };

	operator bool() {
		return false;
	}
};

template<typename T> 
struct is_same <T, T>
{
	enum { value = 1 };

	operator bool() {
		return true;
	}
};
if (is_same<int, decltype(a)>() || is_same<long, decltype(a)>())

Ein Licht geht auf. Die Mannigfaltigkeit der Syntaxkombination treibt mich nochmal in den Wahnsinn. Immer wieder faszinierend angewandte Grundlagen in neuen Kombinationen zu sehen.
Danke für das Bsp. und Erklärung.
Jetzt muss ich mal in Ruhe überlegen wie du das mit dem durchreichen ohne decltype meinst ....

Ich meinte ein Funktions-Template in diesem Fall. Kein Klassen-Template

Header namens Templates.h anlegen:

template<typename T, typename U>
struct is_same
{
  enum { value = 0 };

  operator bool() {
    return false;
  }
};

template<typename T>
struct is_same <T, T>
{
  enum { value = 1 };

  operator bool() {
    return true;
  }
};

template<typename T>
bool is_integer(T value)
{
  return is_same<int, T>() || is_same<long, T>();
}

Dann:

#include "Templates.h"

void setup(void) 
{
  Serial.begin(9600);

  int a = 1;

  if (is_integer(a))
    Serial.print("Ist ein Integer");
  else
    Serial.print("Ist kein Integer");
}

void loop(void) 
{
}

Und wie gesagt müssen eigentliche alle Integer Typen in diese Oder-Verknüpfung damit es richtig geht

Hallo,

ganz ehrlich, so schnell wäre ich nicht darauf gekommen. Die Template Denkweise ist schon anders aber verschachtelt nochmals komplizierter. Das es wirklich ohne decltype machbar ist konnte ich mir nicht wirklich vorstellen. Dachte immer noch irgendwie muss man doch den Typ vorher ermitteln um dann vergleichen zu können. Danke das du mir es verraten hast. :slight_smile:

Die Template Denkweise ist schon anders aber verschachtelt nochmals komplizierter.

Ich glaube, es ist noch viel dramatischer, als du dir jetzt schon vorstellen kannst....

Mit geraumer Verspätung, die C++ Templates waren schon längst in der Welt, hat irgend einer gemerkt, dass die Template Geschichte "Turing vollständig" ist.
Die "Turing Vollständigkeit" war wohl niemals Teil des Plans.
Aber dennoch, war das wie ein Dammbruch.

So haben wir jetzt in C++ sowas wie eine zweite Sprache, in der wir funktionale (Sub)Sprachen definieren und zur Kompilezeit ausführen können.

(War das richtig formuliert? denn ganz sattelfest bin ich da noch nicht)

Der eigentliche Witz ist dass von dem Code kaum was ausgeführt wird. Der Compiler löst das auf und setzt direkt das Ergebnis ein.

Bei anderen, größeren Template-Funktionen sieht man auch schön wie die als Code Generatoren arbeiten. Immer wenn man einen weiteren Datentyp verwendet steigt der Verbrauch an Flash, weil eine weitere Funktion erzeugt werden muss.

Ja… man kann sich schon einen “Code Bloat” mit Templates einfangen.
Mit deiner Methode hier allerdings eher nicht.
Da verschwindest alles… bis auf 1 ASM Statement, um das Resultat in ein Register zu bekommen.

Übrigens, eine kleine Verbesserung:

bool is_integer(T value)
Das value erzeugt eine unused Warnung

bool is_integer(T)
So ist sie weg.

Etwas klarer kann man es evtl so machen

constexpr bool is_integer(T)
Helfen tuts allerdings nicht, der Kompiler dampft es sowieso ein.


Manchmal kann ich mich auch nicht entscheiden, was schöner ist, wenn man denn mal die Wahl hat.

Rekursion zur Kompilezeit:
Hier mal eine gefundene Fakultätsberechnung als Template Variante, und umgebaut zu einer constexpr Funktion.
Mir erscheint die Funktion irgendwie “intuitiver”.

Irgendwelche Unterschiede im generierten Code gibt es natürlich nicht.
Zumindest solange dort Literale/Konstanten verwendet werden.
Mit Variablen funktioniert die Templatevariante nicht, und die Funktion kann einem dann den Stack überfluten.

#include <Streaming.h>

// -------------------

template <int N>
struct Factorial 
{
  enum { value = N * Factorial<N-1>::value };
};
 
template <>
struct Factorial<0> 
{
  enum { value = 1 } ;
};

// -------------------


constexpr int fakultaet(const int n)
{
  return 0 == n ? 1 :  n * fakultaet(n-1);
}

// -------------------



void setup() 
{
  Serial.begin(9600);
  Serial << "Start " << __FILE__ << endl;

  Serial << Factorial<5>::value  << endl;
  Serial << fakultaet(5)         << endl;
}

void loop() 
{
}

Übrigens, eine kleine Verbesserung:

bool is_integer(T value)
Das value erzeugt eine unused Warnung

Dachte ich mir auch erst :o Aber ich verwende Visual Micro und da wird es nicht angemeckert :o Keine Ahnung was da abläuft.
Die Variable zu benennen ist aber so oder so überflüssig

Bei mir avr-gcc-9.1.0 mit -std=gnu++17 und -fno-sized-deallocation, ansonsten Arduino Standard
Vielleicht ist das ja etwas pingeliger.

Hallo,

Serenifly, ohne Werbung machen zu wollen, kannst ja einmal meine avr-gcc 9.2. Toolchain probieren? :slight_smile: Wenn die in Atmel Studio funktioniert sollte sie auch in deiner Visual Studio Umgebung funktionieren.

Templates. Mit selbst gebauten einfachen Funktions- und Klassentemplates komme ich mittlerweile gut klar denke ich. Fremde geschachtelte sind wie immer so eine Sache, wie mit allen fremden Code. Aber mit der prima Erklärung ist das Rätsel gelöst wurden.

Mit der Turing Fähigkeit kämpfe ich immer noch. Zur Zeit ist es bei mir so. Mache ich aus einer Klasse ein Klassentemplate mit dem Hintergrund universell zu sein, folgt einhergehend automatisch diese wundersame Optimierung im RAM/Flashverbrauch. Mache ich das Template mit dem Hintergrund RAM/Flash Einsparung gehts schief. Bei meiner MCP23017 Lib gehts deswegen auch nicht weiter. Bei meiner aktuellen Spielerei einer universellen seriellen Ausgabe mit Template statt vielen Funktionsüberladungen gabs schon ziemlich am Anfang diese wundersame magische Optimierung. Kurzum, ich kann es noch nicht steuern. Mir fehlt noch der entscheidende Zauberspruch. :wink: Ich übe/probiere weiter.

Der Kram war auch fremd. Ist im Prinzip ist das genau das was in ::std implementiert ist:
https://en.cppreference.com/w/cpp/types/is_same
Einfache Sachen kann man sich da manchmal selbst nachbauen wenn man weiß wie es gemacht wird

Das sieht man auch dass die Funktionen eigentlich constexpr sein sollten. Spielt hier aber keine Rolle.

Das von combie stammt von Wikipedia:

In dem Artikel geht es um das allgemeine Prinzip. Statt ausführbare Programme im klassischen Sinn schreibt man Code der nur anderen Code erzeugt