Forum: Compiler & IDEs Interessante g++ Erweiterung für user-defined literals


von Wilhelm M. (wimalopaan)


Lesenswert?

Hallo zusammen,

ich bin auf eine interessante clang++/g++-Erweiterung gestoßen, die auch 
im avr-g++ enthalten ist und damit auch für bare-metal ganz iterressant 
erscheint. Es geht um den user-defined-literal Operator für 
string-literale  als variadisches template. Damit lässt sich die 
Umwandlung schon zur Compile-Zeit machen und so ein DT für Strings im 
Flash darstellen. Im C++-14 Standard ist bislang nur der raw-operator 
für Integrale/ Floats definiert.

Forgendes Beispiel mach das deutlich:
1
#include "console.h"
2
#include "simavr.h"
3
#include "pgmstring.h"
4
5
using terminal = SimAVR;
6
7
namespace std {
8
    std::basic_ostream<terminal> cout;
9
    std::lineTerminator<CRLF> endl;
10
}
11
12
int main()
13
{
14
   auto fs1 = "xyz"_pgm;
15
   std::cout << fs1 << std::endl;
16
   std::cout << "abce"_pgm << std::endl;
17
}

Alle Strings mit dem Suffix _pgm werden zu Klassen mit einem statischen 
Datenelement, dass dann im flash liegt. Durch entsprechende 
Funktionsüberladung (hier der op<<) kann man sie auch gleich ausgeben.

Das witzige dabei ist, dass ja ein Template zusammen mit seiner 
Parametrierung ein DT ist. Also: unterschiedliche Stringliterale ergeben 
unterschiedliche statics im flash, gleich Stringliterale ergeben 
dasselbe(!)
 static im flash.

Die Implementierung sieht so aus:
1
#pragma once
2
3
#include <avr/pgmspace.h>
4
5
template<typename C, C... CC>
6
struct PgmString {
7
    static constexpr const char* str(){
8
        return &data[0];
9
    }
10
    static constexpr const char data[] PROGMEM = {CC..., '\0'};
11
};
12
template<typename C, C... CC>
13
constexpr const char PgmString<C, CC...>::data[] PROGMEM;
14
15
template<typename C, C... CC>
16
constexpr PgmString<C, CC...> operator"" _pgm(){
17
    return PgmString<C, CC...>();
18
}

Sieht jedenfalls ganz vielversprechend aus.
RFC.

: Verschoben durch Admin
von Wilhelm M. (wimalopaan)


Lesenswert?

Nachtrag: wie oben schon geschrieben ist die der oft ins Feld geführte 
Code-Bloat <= 0.

von tictactoe (Gast)


Lesenswert?

Könnte im Prinzip funktionieren. Allerdings scheint mir die str() 
Funktion unbrauchbar: Der Pointer, der zurückgegeben wird, zeigt ins 
Flash, aber wird vom Aufrufer wie ein normaler RAM-Pointer behandelt. 
Das wird nicht gut gehen. Es wird wohl notwendig sein, operator<<() und 
alle weiteren Nutzungsszenarien als Member-Funktionen zu implementieren, 
damit der String mittels pgm_read_byte() ausgelesen wird.

von Wilhelm M. (wimalopaan)


Lesenswert?

Es funktioniert so wie es da steht.

Der Typ ist dann PgmString wie Du siehst, und dafür ist op<< überladen.

von Carl D. (jcw2)


Lesenswert?

Aber bisher nur mit "simavr.h", richtig? Ich bin mal gespannt, ob der 
GCC bereit ist data[] in den Flash zu legen.

Ich hab da auch schon mit gespielt und geben Zeiten/Frequenzen nun so 
an:
10_mSec, 1.5_Sec, 17_kHz, ...
Am Ende steht immer ein "constexpr unsigned long" mit den notwendigen 
Takten abgeleitet aus F_CPU. Da muß man sich das Umrechnen nur einmal 
ausdenken und schreit dann zukünftig was man eigentlich meinte.
Geht aber halt nur, wenn man auf C noch eins draufsattelt ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Geht auch im echten Leben ;-)

Die literal-operatoren sollten eigene DT (etwa Hertz oder MegaHertz oder 
Milliseconds ...) zurückgeben. Dann kann man sich die anderen Operatoren 
auch schreiben und so ein ganzen physikalisches System bauen.

Also: 1_MHz / 1KHz -> 1000

Aber das ist mittlerweile ja normal in C++ ...

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

Nur der Vollständigkeit halber:
> Im C++-14 Standard ist bislang nur der raw-operator für Integrale/ Floats
> definiert.

"User Defined String Literals" sind ein C++14 Feature und keine GCC 
Erweiterung.
 Aber dein Code hat mich dazu gebracht mir das nochmal im Detail bei 
cppreference.com anzuschauen. Das ich das auch variadic-template'n kann, 
hatte ich noch gar nicht entdeckt. Reduziert doppelten Code, denn 
Integer- oder Float-Versionen meiner Zeiten/Frequenzen unterscheiden 
sich nur in den Parametern. Quasi negativer Source-Code-Bloat ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:
> Nur der Vollständigkeit halber:
>> Im C++-14 Standard ist bislang nur der raw-operator für Integrale/ Floats
>> definiert.
>
> "User Defined String Literals" sind ein C++14 Feature und keine GCC
> Erweiterung.

Bitte genau lesen: der "template literal operator" (!!!) für 
string-literale, also "text"_bla ist eine gcc/clang Erweiterung!

Der "template literal operator" für ganzzahlige / fließkomma-Literale 
ist ab C++11 da (etwa 100_km)

Das ist ein Unterschied!

s.a: http://en.cppreference.com/w/cpp/language/user_literal

: Bearbeitet durch User
von tictactoe (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Der Typ ist dann PgmString wie Du siehst, und dafür ist op<< überladen.

Den Typ sehe ich, aber dass operator<< überladen wäre, geht aus deinem 
Code nicht hervor.

Ah, jetzt sehe ich, dass du da etwas mit basic_ostream<terminal> 
zauberst. Damit habe ich keine Erfahrung. Ich muss glauben, was du 
sagst...

Insgesamt ist dein Vorgehen natürlich eine gute Idee.

von Wilhelm M. (wimalopaan)


Lesenswert?

tictactoe schrieb:
> Wilhelm M. schrieb:
>> Der Typ ist dann PgmString wie Du siehst, und dafür ist op<< überladen.
>
> Den Typ sehe ich, aber dass operator<< überladen wäre, geht aus deinem
> Code nicht hervor.
>
> Ah, jetzt sehe ich, dass du da etwas mit basic_ostream<terminal>
> zauberst. Damit habe ich keine Erfahrung.

Hier ist
1
terminal
 einfach eine Wrapper-Klasse um den Debug-Port des simavr. Generell kann 
man aber einfach irgendeinen USART zum Terminal erklären:
1
using terminal = AVR::Usart<0>;

von Carl D. (jcw2)


Lesenswert?

Nochmal auf dein FlashString-Beispiel zurückzukommend:
- was ist die gcc-Erweiterung und wie schaltet man sie ein?

Mein gcc 6.2 -std=c++14 legt zwar den String brav in den Flash, gleich 
nach den Int-Vektoren, aber kann darauf nicht zugreifen.

- was steht in deiner "console.h" und in "simavr.h"?
- woher kommt std::basic_ostream ?

Welche clang Version hat ein AVR Backend?

Bitte überzeug mich, daß du recht hast.

von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:
> Nochmal auf dein FlashString-Beispiel zurückzukommend:
> - was ist die gcc-Erweiterung und wie schaltet man sie ein?

gar nicht.
Wenn Du sie benutzt und -Wgnu-string-literal-operator-template 
verwendest bekommst Du auch die passende Warung.

> Mein gcc 6.2 -std=c++14 legt zwar den String brav in den Flash, gleich
> nach den Int-Vektoren, aber kann darauf nicht zugreifen.

Warum nicht?
Falls es um den op<< geht, muss Du dann natürlich pgm_read_byte() 
verwenden.

> - was steht in deiner "console.h" und in "simavr.h"?

die überladenen op<<

> - woher kommt std::basic_ostream ?

von mir ;-)

> Welche clang Version hat ein AVR Backend?

keine, das bezog sich ja nur auf die extension, die beide unterstützen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Wilhelm M. schrieb:
> Carl D. schrieb:
>> Nochmal auf dein FlashString-Beispiel zurückzukommend:
>> - was ist die gcc-Erweiterung und wie schaltet man sie ein?
>
> gar nicht.
> Wenn Du sie benutzt und -Wgnu-string-literal-operator-template
> verwendest bekommst Du auch die passende Warung.

Sorry, das ist eine Option von clang++ und nicht vom g++, also auch 
nicht vom avr-g++

von Carl D. (jcw2)


Lesenswert?

was ich auch immer aktuell anders gemacht habe, nun geht's.

Jetzt werd ich mir mal ostreams zusammenkopieren müssen.

fast geschaft ;-)

von Carl D. (jcw2)


Lesenswert?

es will nicht:
1
int main() {
2
  auto fs1 = "xyz"_pgm;          // hinterläst brav string im Flash
3
  v8 = pgm_read_byte(fs1.str()); // liest brav ein byte aus Flash
4
5
  std::cout << "ABC"_pgm;        // gibt ABC über "copy&paste" ostrem aus
6
                                 // Danke an Konstantin Chizhov
7
  std::cout << fsl;              // ???
8
...
ergibt:  error: 'fsl' was not declared in this scope

Angesichts der Tatsache, daß jeder PgmString einen eigenen Typ bekommt, 
funktioniert auch das:
1
int main() {
2
  using fs1 = "xyz"_pgm;         // hinterläst brav string im Flash
3
  v8 = pgm_read_byte(fs1::str());// liest brav ein byte aus Flash
4
5
  std::cout << "ABC"_pgm;        // gibt ABC über "copy&paste" ostrem aus
6
                                 // Danke an Konstantin Chizhov
7
  std::cout << fsl;              // ???
8
...
liefert aber exakt die gleiche Fehlermeldung.

In welchem Scope könne der Compiler das Symbol fsl, ob Variable oder 
Typ, egal, denn versteckt haben?

Nebenbei: der SyntaxHighlighter kann kein C++ >= 11 (using)

: Bearbeitet durch User
von Guest (Gast)


Lesenswert?

fsl!=fs1

von Carl D. (jcw2)


Lesenswert?

Guest schrieb:
> fsl!=fs1

Schande über mich!!!

einmal (mit alten Augen) nicht kopiert, sondern selbst (ab-)geschrieben.
Jetzt versteh ich auch den komischen Namen.
Nicht "fussel", sondern "FlashString1".
Ich brauch eine bessere Brille, besseres Display, bessere Fonts ...

Danke für den anonymen Tip!

von Wilhelm M. (wimalopaan)


Lesenswert?

Oder einfach mal in der Zeile den Fehler suchen, die vom Compiler 
angezeigt wird ;-) Oder einen besseren Editor verwenden (vim, emacs) 
oder IDE (qtcreator) ...

von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:
> es will nicht:
>
1
int main() {
2
>   auto fs1 = "xyz"_pgm;          // hinterläst brav string im Flash
3
>   v8 = pgm_read_byte(fs1.str()); // liest brav ein byte aus Flash
4
> 
5
>   std::cout << "ABC"_pgm;        // gibt ABC über "copy&paste" ostrem 
6
> aus
7
>                                  // Danke an Konstantin Chizhov
8
>   std::cout << fsl;              // ???
9
> ...

Was ist denn "copy&paste" ostream?

von Carl D. (jcw2)


Lesenswert?

Wilhelm M. schrieb:
> Was ist denn "copy&paste" ostream?

naja, eine Handvoll Includes, die ich mir bei Mcucpp geborgt hab.
ostream<terminal> (mit terminal::put auf PortB, damit das ganze nicht 
wegoptimiert wird) lief so innerhalb 5 Minuten und ein operator<< für 
FlashString war in der Zeit schon mit drin.
Nur die 1 von einem l zu unterscheiden hab ich ohne fremde Hilfe nicht 
hinbekommen. ;-)
(wobei, bei meinem alten iPad in Normalgröße unterscheiden die sich auch 
nur um ein Pixel, wie ich gerade nach Vergrößern sehe)

von Wilhelm M. (wimalopaan)


Lesenswert?

Na wunderbar!!!

Wie gesagt (in einem anderen Thread), bin ich dabei, meine gesamte 
uC-Code-Basis auf C++14/17/20 umzustellen und komme aus der Begeisterung 
gar nicht mehr heraus ;-)

von Carl D. (jcw2)


Lesenswert?

Wilhelm M. schrieb:
> Na wunderbar!!!
>
> Wie gesagt (in einem anderen Thread), bin ich dabei, meine gesamte
> uC-Code-Basis auf C++14/17/20 umzustellen und komme aus der Begeisterung
> gar nicht mehr heraus ;-)

Das Gefühl herrscht bei mir auch gerade (mal wieder).
Hatte ich vor C++11 schon mal versucht und mich in <loki> verfangen.

von Nico W. (nico_w)


Lesenswert?

Warum eigentlich so kompliziert und nicht einfach
1
__flash
 nutzen?

von Wilhelm M. (wimalopaan)


Lesenswert?

__flash ist ein non-standard keyword von IAR.

PROGMEM ist dasselbe für avr-gcc.

von Nico W. (nico_w)


Lesenswert?

Wilhelm M. schrieb:
> __flash ist ein non-standard keyword von IAR.

Ab 4.7 oder so auch bei GCC.

von Wilhelm M. (wimalopaan)


Lesenswert?

Ok, prima.
Und was sollte dann die Bemerkung?

von Nico W. (nico_w)


Lesenswert?

Wilhelm M. schrieb:
> Und was sollte dann die Bemerkung?

Vielleicht habe ich ja auch nicht erkannt, wozu das ganz ist. (?)

von Wilhelm M. (wimalopaan)


Lesenswert?

Das Klassentemplate PgmString<> ist dazu da, zu Compile-Zeit ein 
C-String mit dem suffix _pgm, etwa "abc"_pgm im Flash abzulegen und dies 
dann so zu kapseln, dass man sich nicht mehr um das lesen aus dem Flash 
mit pgm_read_byte(), etc. kümmern muss.

Zusätzlich: wenn im Programm mehrfach bspw. "abc"_pgm vorkommt, so wird 
im Flash das nur einmal abgelegt, ohne dass ich mich als Programmierer 
drum kümmern muss.

von Carl D. (jcw2)


Lesenswert?

Nico W. schrieb:
> Warum eigentlich so kompliziert und nicht einfach__flash nutzen?

Weil das eine nur für C verfügbare Erweiterung ist, die auf "named 
memspaces" auf der EmbededC Definition aufsetzt. Für C++ gibt's das 
leider nicht. Harward-Architektur ist leider (noch(Hoffnung)) nicht die 
Zielgruppe des ISO-CPP-Gremiums. Und GCC hat versprochen, sich an den 
Standard zu halten.

Man könnte sonst z.B. nicht nur den __flash MemorySpace, sondern auch 
einen __eeprom MemorySpace im AVR-GCC bauen und dann statt LDM 
entsprechende Calls in die EEProm-Leseroutinen machen.

Wilhelm M. schrieb:
> __flash ist ein non-standard keyword von IAR.

nicht mehr:

6.16 Named Address Spaces

As an extension, GNU C supports named address spaces as defined in the 
N1275 draft of ISO/IEC DTR 18037. Support for named address spaces in 
GCC will evolve as the draft technical report changes. Calling 
conventions for any target might also change. At present, only the AVR, 
SPU, M32C, RL78, and x86 targets support address spaces other than the 
generic address space.

Address space identifiers may be used exactly like any other C type 
qualifier (e.g., const or volatile). See the N1275 document for more 
details.

von Wilhelm M. (wimalopaan)


Lesenswert?

Oh, dass das der gcc macht als Erweiterung, ist mir entgangen ...

Naja, der g++ macht's halt nicht. Auf der anderen Seite hat man sich das 
ja nun auch schnell gekapselt. Selbst wenn man das literal operator 
template for string-literals nicht verwenden würde.

von Carl D. (jcw2)


Lesenswert?

Wilhelm M. schrieb:
> Zusätzlich: wenn im Programm mehrfach bspw. "abc"_pgm vorkommt, so wird
> im Flash das nur einmal abgelegt, ohne dass ich mich als Programmierer
> drum kümmern muss.

thumbs up!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wie würde man denn auf solche Strings zugreifen? Etwa so?
1
template<typename C, C... CC>
2
struct PgmString
3
{
4
  ...
5
6
  char operator[] (size_t pos) const
7
  {
8
    if (__builtin_constant_p (pos))
9
      return char_at (pos);
10
    else
11
      return pgm_read_byte (data + pos);
12
  }
13
14
private:
15
  static constexpr char char_at (size_t pos)
16
  {
17
    return data[pos];
18
  }
19
};

Das eigentliche Problem scheint aber zu sein, mit solchen Strings zu 
hantieren und sie zu übergeben:
1
??? get_ok_str (bool ok)
2
{
3
  return ok ? "good"_pgm : "bad"_pgm;
4
  }
Das kann natürlich nicht funktionieren, weil die Strings 
untersciedlichen Typ haben.  Und sobald man die .str() Methode verwendet 
hat man eh verloren:  Dann muss pgm_read wieder händisch gemacht werden, 
und das ganze Template-Geraffel ist nur eine extrem unübersichtliche 
Variante einen Stringf ins Flash zu bekommen.

Wie wüde denn ein Beispiel mit Wochentagen aussehen, d.h. ein Lookup von 
strings, das einen Index auf einen Wochentag-String abbildet?

Oder allgemeiner eine LUT mit Integern?

von Wilhelm M. (wimalopaan)


Lesenswert?

Da stellt sich die Frage: was möchtest Du dann machen? Sollen die 
Strings im Flash als immutable bleiben oder möchtest Du sie im RAM 
haben.

Sollen Sie im Flash bleiben bittet sich ein Thin-Wrapper an:
1
class PgmStringView {
2
public:
3
    const char* p = nullptr;
4
};
5
6
class MessageCatalog {
7
public:
8
    enum Id {first= 0, second, NumberOfMessages};
9
    static PgmStringView message(enum Id index) {
10
        return messages[index];
11
    }
12
13
private:
14
    static  const PgmStringView messages[];
15
};
16
17
const PgmStringView MessageCatalog::messages[NumberOfMessages] = {
18
    "abc"_pgm,
19
    "def"_pgm,
20
};

und in PgmString den Typwandlungs-Op
1
    constexpr operator PgmStringView() const {
2
        return {&data[0]};
3
    }


Möchtest Du sie im RAM haben, würde ich einen StringBuffer nehmen, der 
ein Typ-Wandlungs ctor für PgmStringView hat.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Da stellt sich die Frage: was möchtest Du dann machen?

Ich würde gern ein besseres Verständnis dieser C++ Abartigkeiten[tm] 
bekommen und ob sich die Terz irgendwie lohnt.  Wenn ich gefühlt 30 
Zeilen Code brauche, um einen 1-Zeiler in C wie
1
const __flash data_t data[] = { ... };
abzubilden, dann sinkt die Attraktivität schon spürbar.  Um so mehr, als 
ich selbst nicht auf all diese Konstrukte gekommen wäre.

> Sollen die
> Strings im Flash als immutable bleiben oder möchtest Du sie im RAM
> haben.

Weder noch, mich interessiert, wie ein allgemeiner Ersatz von __flash 
sinnvoll aussehen würde.  Bei __flash ist die Verwendung egal; ich 
verwende das Zeug eben wie normale Daten mit normalem C-Code.  Was 
angepasst werden muss sind Zeiger, die entsprechend qualifiziert werden 
müssen.

Und ein Großteil der Daten sind oft eben keine Strings, sondern andere 
Daten wie Strukturen oder Arrays davon.


> Sollen Sie im Flash bleiben bittet sich ein Thin-Wrapper an:
>
> [c]
> class PgmStringView {
> public:
>     const char* p = nullptr;
> };

Wozu brauche ich denn diesen schon wieder?  Der macht doch das gleiche 
wie .str().

> class MessageCatalog {
> public:
>     enum Id {first= 0, second, NumberOfMessages};
>     static PgmStringView message(enum Id index) {
>         return messages[index];
>     }
>
> private:
>     static  const PgmStringView messages[];
> };
>
> const PgmStringView MessageCatalog::messages[NumberOfMessages] = {
>     "abc"_pgm,
>     "def"_pgm,
> };

D.h. es ist nicht möglich, Arrays von PgmStringView anzulegen, und 
desshalb muss stattdessen eine neue Klasse MessageCatalog implementiert 
werden?

Aber das einzige was ich jetzt mit den Strings jetzt tun kann, ist doch 
einen "const char*" zu bekommen, und dann mit pgm_read zuzugreifen?

Wenn ich sowas verwende, dann wäre ein Schema sinnvoll, das unabhängig 
von der Verwendung ist.  Ansonsten ist man ja ständig dabei, die 
Applikation und alles umzustricken, nur weil sich an einer Stelle die 
Verwendung ändert.

von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> Wilhelm M. schrieb:
>> Da stellt sich die Frage: was möchtest Du dann machen?
>
> Ich würde gern ein besseres Verständnis dieser C++ Abartigkeiten[tm]
> bekommen und ob sich die Terz irgendwie lohnt.  Wenn ich gefühlt 30
> Zeilen Code brauche, um einen 1-Zeiler in C wie
>
1
const __flash data_t data[] = { ... };
> abzubilden, dann sinkt die Attraktivität schon spürbar.  Um so mehr, als
> ich selbst nicht auf all diese Konstrukte gekommen wäre.

Nun ja, das wär in C++ auch wirklich schön. Aber das C++-Committee wie 
auch das C-Committee haben den Draft-xxxx (Nummer weiß ich grad nicht) 
nicht akzeptiert. Allerdings ist der Draft im gcc und leider nicht im 
g++ drin.

> Wozu brauche ich denn diesen schon wieder?  Der macht doch das gleiche
> wie .str().

Dem const char* sehe ich aber nicht an, dass er anders behandelt werden 
muss / sollte. Primitive DT wie int, double, etc. und char* tragen eben 
keine Semantik. Dafür sind dann solche "domänen-spezifischen" DT da. Ein 
PgmStringView muss ich ja mit pgm_read_... bearbeiten.


> D.h. es ist nicht möglich, Arrays von PgmStringView anzulegen, und
> desshalb muss stattdessen eine neue Klasse MessageCatalog implementiert
> werden?

Doch. Steht ja da.


> Aber das einzige was ich jetzt mit den Strings jetzt tun kann, ist doch
> einen "const char*" zu bekommen, und dann mit pgm_read zuzugreifen?

s.o.

Man braucht sich dann aber bei der Verwendung nicht mehr drum zu 
kümmern, ob es nun Daten aus dem RAM oder Flash sind. Also so ähnlich 
wie bei __flash.

Summa summarum:

1) das Attribute __flash wäre schön im g++, ist aber nicht drin, und 
wird auch nicht reinkommen, sofern der Draft ein Draft bleibt

2) Einen (internationalisierten) Nachrichtenkatalog aus PgmString zu 
bauen ist eigentlich nicht sinnvoll. Das macht man besser mit nackten 
C-Strings im progmen, die dann von der Klasse MessageCatalog o.ä. in ein 
PgmStringView (ist ja auch nur ein Zeiger) gewandelt wird. Denn die 
Strings im katalog werden wohl alle unterschiedlich sein.

3) Wahrscheinlich ist eine Mischlösung: MessageCatalog in C-Quelldatei 
mit __flash und ThinWrapper in C++ sinnvoll(er).

von Oliver S. (oliverso)


Lesenswert?

Johann L. schrieb:
> Wenn ich gefühlt 30
> Zeilen Code brauche, um einen 1-Zeiler in C wie

Das ist die Faszination von Templates. Ganz gegen den immer noch 
allgemeingültigen Spruch "premature optimization is the root of all 
evil" verführen die genau dazu.

Ich hab den Versuch auch schon hinter mit, ob es nicht gelingt, das der 
Compiler an die 1000 Zeilen C++-Templatecode in nur eine einzige 
Assembleranweisung übersetzt. Und ja, das geht ;)

Der praktische Sinn bleibt bei solchen Fingerübungen schnell auf der 
Strecke. Aber lustig ists schon.

Oliver

von temp (Gast)


Lesenswert?

Oliver S. schrieb:
> Ich hab den Versuch auch schon hinter mit, ob es nicht gelingt, das der
> Compiler an die 1000 Zeilen C++-Templatecode in nur eine einzige
> Assembleranweisung übersetzt. Und ja, das geht ;)
>
> Der praktische Sinn bleibt bei solchen Fingerübungen schnell auf der
> Strecke. Aber lustig ists schon.

Ich würde sogar soweit gehen und behaupten, so etwas trägt dazu bei, 
dass der Code noch viel unleserlicher wird als ohne diese Templates. 
Gerade bei excessiver Verwendung von überladenen Operatoren verliert man 
schnell den Überblick. Ich nutze auch C++, aber nicht um jeden Preis. 
Wenn ein anderer stundenlang braucht um meine geistigen Ergüsse zu 
verstehen habe ich was falsch gemacht. Jeder der AVRs programmiert hat 
sich mit der Besonderheit der Strings im Flash abgefunden und kann damit 
umgehen. Warum muss man da wieder ein neues Rad erfinden?

von Carl D. (jcw2)


Lesenswert?

Johann L. schrieb:
> Wenn ich gefühlt 30
> Zeilen Code brauche, um einen 1-Zeiler in C wie

Das ist (näherungsweise) so, als würde man deine 4-stellige Anzahl 
Zeilen im AVR-Backend gegen die C eingesparten Zeilen Assembler-Code 
verrechnen wollen. Man versucht (manchmal erfolgreich) in einen Header 
Berge von Templates und ConstExpr's zu stecken, die in der eigentlichen 
Source gut lesbaren Code hinterlassen. Das Haeder-File muß man, wenn es 
mal funktioniert nicht mehr anschauen, wenn das zu stark erschreckt.

Zu der Frage "wie komm ich an die FlashStrings": Man würde bei so was 
versuchen den "="-Operator zu überladen, nur geht das hier nicht, denn 
dieser muß eine Member-Funktion sein, d.h. Ziel ist das aktuelle Objekt 
"*this". Variabel kann der Quellwert sein. Da aber ein solcher "Wert" 
wie hier, ein Typ mit einen Statischen Datenbereich (der bei AVR auch 
das Attribute "Flash" haben kann) ist, gibt es kein Objekt und selbst 
wenn, es soll ja Quelle und nicht Ziel sein.
Andere Operatoren können mit 2 Parametern definiert werden, z.B.:
1
template<typename D,~> void operator<< (T& dst, FlashConst<~>& src) {
2
  pgm_read_block((void*)&dst, (const void *)src.addr(), sizeof(D));
3
}

(Das ist nur das Konzept und hat Auskassungen (die beiden "~") sowie 
wurde auf dem Pad getippt, hat also sicher Syntax-Probleme)

Das geht für die Strings des TO mit einer operator<< Variante, die in 
eine Stream schreibt, ich hab das gestern Abend auch noch (mit jeweils 
10 Zeilen Code) für POD, sowie POD[] hinbekommen. Wenn ich wieder am 
Rechner bin mehr.

Bei Strukturen häng ich an dem Problem nicht-Typ-Gleiche 
Initialisierung-Parameter zur Compile-Time in eine statisches Variable 
zu packen. Mit n-mal der gleiche Typ geht das leicht.

Seit gestern ist mir auch klar, warum man die shift-Operatoren so gerne 
überläd. ;-)


Ps: das coole ist, ich kann diese Konstante n-mal neu definieren, also 
an 100 Stellen das Literal "OK"_pgm ausgeben. Es bleibt ein (vom Inhalt 
abhängiger) Typ mit einer einzigen statischen Konstante im Flash.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:
> Johann L. schrieb:
>> Wenn ich gefühlt 30
>> Zeilen Code brauche, um einen 1-Zeiler in C wie
>
> Ps: das coole ist, ich kann diese Konstante n-mal neu definieren, also
> an 100 Stellen das Literal "OK"_pgm ausgeben. Es bleibt ein (vom Inhalt
> abhängiger) Typ mit einer einzigen statischen Konstante im Flash.

Genau darum ging es mir ja!!! Nichts anderes zunächst einmal. Weil ich 
auch oft - sinnvoll oder nicht - an vielen Stellen im Code 
String-Literale hingeschrieben habe.

Und ich würde es nicht für einen Message-Catalog einsetzen. Das mache 
ich ähnlich wie bei "normalen, hosted" C++-Applikationen: man hat 
message-identifier die je nach locale auf die richtige 
Meldung/Formatierung/Währungssymbol/you-name-it abgebildet werden.

Aber vor allem geht es darum: ein Interface soll leicht richtig und nur 
sehr schwer falsch zu benutzen sein. Es soll nicht möglich sein, einen 
C-String, der im flash ist, wie einen C-String im RAM zu behandeln. Das 
geht nämlich bei g++ (s.o.) wegen des fehlenden __flash Attributes 
schief ...

von Carl D. (jcw2)


Lesenswert?

Wilhelm M. schrieb:
> Aber vor allem geht es darum: ein Interface soll leicht richtig und nur
> sehr schwer falsch zu benutzen sein. Es soll nicht möglich sein, einen
> C-String, der im flash ist, wie einen C-String im RAM zu behandeln.
Ich hab nun 2 Abende damit rumgespielt und muß das vielleicht mal in 
einen Artikel packen. Kurz (und wieder auf dem Pad und deshalb nur als 
Gedächtnisprotokol):
1
// Strings; Dir bekannt ;-)  sind eigentlich Character Arrays
2
char ca[10];
3
ca << "Flash"_pgm; // Lesen, Länge ergibt sich aus Quelle UND Ziel
4
5
// Flash-Konstanten; POD und Arrays aus POD
6
FlashConst<uint16_t, 42> f_int{};
7
int i;
8
i << f_int;  // Lesen, ...
9
10
FlashConstArray<uint16_t,10,  1,2,3,4,5,6,7,8,9,10> f_int_array{};
11
...
12
13
// EEProm-Variablen; POD und Arrays aus POD
14
EEProm<char,'x'> ee_x{} EEMEM; // hat kein static data, deshalb EEMEM
15
char ch;
16
ch << ee_x;  // Lesen
17
ee_x << 'X'; // Schreiben
18
19
EEPromArray<float,5,  1.2,1.5,1.6,2.4,4> ee_f_array EEMEM;
20
float coef[4];
21
coef << ee_f_array;
22
coef[2] = 3.7;
23
ee_f_array << coef;
24
...

Viele Detailfragen sind noch offen, aber es geht prinzipiell.

> Das geht nämlich bei g++ (s.o.) wegen des fehlenden __flash Attributes
> schief ...
__flash ist aber kein Attribute, sondern ein Type-Qualifier, so wie 
const und volatile. Das ist somit Teil des Sprachstandards und über den 
wacht das Kommitee.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:
> Wilhelm M. schrieb:

>
>
1
// Strings; Dir bekannt ;-)  sind eigentlich Character Arrays
2
> char ca[10];
3
> ca << "Flash"_pgm; // Lesen, Länge ergibt sich aus Quelle UND Ziel
4
5
Das versuche ich zu vermeiden: keine raw-arrays. In dem Fall ist es bei mir ein StringBuffer.
6
7
> // EEProm-Variablen; POD und Arrays aus POD
8
> EEProm<char,'x'> ee_x{} EEMEM; // hat kein static data, deshalb EEMEM
9
> char ch;
10
> ch << ee_x;  // Lesen
11
> ee_x << 'X'; // Schreiben
12
> 
13
> EEPromArray<float,5,  1.2,1.5,1.6,2.4,4> ee_f_array EEMEM;
14
> float coef[4];
15
> coef << ee_f_array;
16
> coef[2] = 3.7;
17
> ee_f_array << coef;
18
> ...
19
>

Ja, mit EEProm geht das natürlich auch.

> __flash ist aber kein Attribute, sondern ein Type-Qualifier, so wie
> const und volatile. Das ist somit Teil des Sprachstandards und über den
> wacht das Kommitee.

Ja, Du hast natürlich recht: ist ein type qualifier. Allerdings wie 
gesagt ist N1275 ein Draft (!) für C und deswegen nicht in C11 drin, und 
nicht für C++, deswegen nicht in C++11 ff. drin. AFAIK auch nicht für 
C++17/20 scheduled :-(

von MitLeserin (Gast)


Lesenswert?

@Carl Drexler

Bitte die Programm-Schnipsel dieses Threads zu einem compilierbaren 
Beispiel zusammenfassen und den verwendeten Compiler benennen.

Dann könnte ich eventuell frei nach Sesamstrasse die Fragen nach "wer", 
"wie", "was", "wieso", "weshalb", "warum" auch selber beantworten.

Meine aktuellen Randbedingungen:
*******************************
Mcucpp,
Atmel-Studio6.2 und
avr-gcc-8.0_2017-07-19_mingw32

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.