mikrocontroller.net

Forum: PC-Programmierung C++ Codegüte und Operator Overloading


Autor: Johann L. (gjlayde) Benutzerseite
Datum:
Angehängte Dateien:
  • foo.s (2,97 KB, 153 Downloads)

Bewertung
0 lesenswert
nicht lesenswert
Hi, ich versuche mich gerade daran, in C++ Operatoren zu überladen.

Der Code sieht so aus:
class V
{
    public:
        signed char v;
        
        inline V(){};
};

static inline V operator+ (V a, const V b)
{
    a.v += b.v;
    return a;
}

V add (const V a, const V b)
{
    return a+b;
}

V set (void)
{
    V v;
    v.v = 123;
    return v;
}

unsigned int sizeV = sizeof (V);

Leider ist die Codegröße jenseits von Gut und Böse: Alle Operationen 
werden über den Frame bzw. this abgewickelt.

In dem C-Projekt verwende ich 8-Bit Variablen für Fixpunkt-Arithmetik, 
d.h. Q1.8, Q0.8 und 2-dimensionale Vektoren darauf. Das funktioniert 
soweit auch ganz prima und effizient, allerdings wird die C-Quelle durch 
die zig Arithmetik-Makros nicht gerade hübsch. Daher versuche ich mich 
gerade an C++, um Operatoren überladen zu können und so eine 
überschbarere Quelle zu bekommen. Zugegebenermassen hab ich immer einen 
Bogen um C++ gemach -- auf dem PC hab ich immer lieber zu was richtig 
objektorientiertem gegriffen wie Java.

Wie auch immer... Der Code, den GCC für das kleine Beispiel erzeugt, ist 
der Overkill; sowohl die Addition als auch das Setzen der Variablen 
brauchen eigentlich je nur einen Befehl. Überladen, Exceptions, 
virtuelle Methoden, Vererben brauch ich nicht und will sie hier auch 
garnicht -- ich will nur die kompakte Syntax der Operatoren-Überladung 
für + und *.

Was mir aufgefallen ist, ist daß der Konstruktor als weak angelegt wird, 
so daß es nicht geinlint werden kann vom Compiler. Wie vermeidet man 
das?

Bzw. wie ist die Quelle hinzuschreiben, daß C++ sich nicht wegen 
inakzeptabel breitem und langsamen Code raushaut und mit C mithalten 
kann?

: Verschoben durch User
Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
zur codegröße kann ich nichts sagen, aber das du es dem compieler nicht 
gerade leicht machts erkenne ich.

> static inline V operator+ (V a, const V b)

damit wird bei jeder übergeben eine Kopie von a und b angelegt mit alles 
was dazu gehört (Construktor/Destruktor)

viel besser sollte es so sein

class V
{
    public:

        signed char v;

        inline V(){};
        inline V( signed char value): v(value) {};
};

static inline V operator+ (const V& a, const V& b)
{
    return V(a.v + b.v);
};

Autor: High Performer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>zur codegröße kann ich nichts sagen, aber das du es dem compieler nicht
>gerade leicht machts erkenne ich.

Vom OP:

>static inline V operator+ (V a, const V b)
>{
>    a.v += b.v;
>    return a;
>}

Und außerdem zeigt das Beispiel, warum operator overloading geteiltes 
Echo in der Programmiergemeinde auslöst, und mich immer wieder vom 
Wunsch abbringt, sowas auch in Java zu haben:

der OP verändert einen der Operanden der Addition, was natürlich niemand 
erwarten würde. Sobald jemand anderes dann den Code verwendet, wird er 
große Augen machen. Also doch bitte Finger weg vom Überladen von 
Operatoren, wenn man die Kontrakte nicht einhalten will.

>static inline V operator+ (const V& a, const V& b)
>{
>    return V(a.v + b.v);
>};

schon besser. ;-)

Autor: Johann L. (gjlayde) Benutzerseite
Datum:
Angehängte Dateien:
  • foo.s (2,38 KB, 134 Downloads)

Bewertung
0 lesenswert
nicht lesenswert
Peter schrieb:
> zur codegröße kann ich nichts sagen, aber das du es dem compieler nicht
> gerade leicht machts erkenne ich.
>
>> static inline V operator+ (V a, const V b)
>
> damit wird bei jeder übergeben eine Kopie von a und b angelegt mit alles
> was dazu gehört (Construktor/Destruktor)

sizeof(V) ist 1, also ist es nicht nötig, mehr als 1 Byte zu übergeben 
(denk ich mir so al C++ Noob). Zumindest wenn alles andere statisch 
bekannt ist, also kein virtueller Krempel etc.


> viel besser sollte es so sein [...]

Ok, machen wir es explizit:
class V
{
    public:

        signed char v;

        inline V(){};
        inline V (signed char value) : v(value) {};
};

static inline V operator+ (const V& a, const V& b)
{
    return V(a.v + b.v);
};

V add (const V a, const V b)
{
    return a+b;
}

V set (void)
{
    return V(128);
}

unsigned int sizeV = sizeof (V);

ARGL, mann bin ich blöd, ich hatte -Os vergessen. Peinlich. Jetzt 
sieht's schon besser aus :-)

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
High Performer schrieb:
> der OP verändert einen der Operanden der Addition, was natürlich niemand
> erwarten würde. Sobald jemand anderes dann den Code verwendet, wird er
> große Augen machen. Also doch bitte Finger weg vom Überladen von
> Operatoren, wenn man die Kontrakte nicht einhalten will.
das ist hier aber nicht der fall, weil er voher schon eine Kopie von dem 
Object gemacht hat.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
High Performer schrieb:
> Und außerdem zeigt das Beispiel, warum operator overloading geteiltes
> Echo in der Programmiergemeinde auslöst, und mich immer wieder vom
> Wunsch abbringt, sowas auch in Java zu haben:
>
> der OP verändert einen der Operanden der Addition, was natürlich niemand
> erwarten würde.

Er verändert keinen Operanden, auch wenn das ein Java-Programmierer 
nicht erwarten würde, weil der den Unterschied zwischen call-by-value 
und call-by-reference nicht kennt. ;-)

>>static inline V operator+ (const V& a, const V& b)
>>{
>>    return V(a.v + b.v);
>>};
>
> schon besser. ;-)

Nicht besser, aber zumindest auch nicht schechter.

Johann L. schrieb:
>> damit wird bei jeder übergeben eine Kopie von a und b angelegt mit
>> alles was dazu gehört (Construktor/Destruktor)
>
> sizeof(V) ist 1, also ist es nicht nötig, mehr als 1 Byte zu übergeben
> (denk ich mir so al C++ Noob). Zumindest wenn alles andere statisch
> bekannt ist, also kein virtueller Krempel etc.

Da denkst du richtig. Eine Übergabe per Referenz wäre hier sogar 
deutlich ineffizienter als per Kopie, wenn es sich nicht gerade um 
Inline-Funktionen handeln würde, wo der Compiler die Referenzierung 
einfach wegoptimieren kann. Hier sollte es also absolut keinen 
Unterschied machen.
Die meisten C++-Programmierer sind es nur gewöhnt, skalare Typen per 
Kopie und Klassen per Referenz zu übergeben, deshalb macht so eine 
Stelle im Code immer etwas hellhörig.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
>>> damit wird bei jeder übergeben eine Kopie von a und b angelegt mit
>>> alles was dazu gehört (Construktor/Destruktor)
>>
>> sizeof(V) ist 1, also ist es nicht nötig, mehr als 1 Byte zu übergeben
>> (denk ich mir so al C++ Noob). Zumindest wenn alles andere statisch
>> bekannt ist, also kein virtueller Krempel etc.
>
> Da denkst du richtig. Eine Übergabe per Referenz wäre hier sogar
> deutlich ineffizienter als per Kopie, wenn es sich nicht gerade um
> Inline-Funktionen handeln würde, wo der Compiler die Referenzierung
> einfach wegoptimieren kann. Hier sollte es also absolut keinen
> Unterschied machen.
> Die meisten C++-Programmierer sind es nur gewöhnt, skalare Typen per
> Kopie und Klassen per Referenz zu übergeben, deshalb macht so eine
> Stelle im Code immer etwas hellhörig.

Anders geht das doch nicht, oder? Jedenfalls erlaubt C++ es nicht, 
Opertoren für Skalare zu überladen.

BTW, was hat das Thema im Forum "PC-Programmierung" zu suchen?
Es geht doch darum, welchen Code avr-g++ erzeugt. avr-g++ läuft zwar auf 
einem PC, aber das interessiert hier eher weniger. Fokus ist die 
Codeerzeugung von G++ für AVR -- auch wenn's nicht explizit im Thema 
steht.

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:
> Codeerzeugung von G++ für AVR -- auch wenn's nicht explizit im
> Thema steht.
Es steht weder im Titel noch im Text das es um die Codeerzeugung von g++ 
für AVR im speziellem geht. Die Frage hat auch für PC Architekturen und 
andere Compiler Gültigkeit, weil dein Problem nicht der avr-g++ ist 
sonder C++ im allgemeinen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:

>> Die meisten C++-Programmierer sind es nur gewöhnt, skalare Typen per
>> Kopie und Klassen per Referenz zu übergeben, deshalb macht so eine
>> Stelle im Code immer etwas hellhörig.
>
> Anders geht das doch nicht, oder? Jedenfalls erlaubt C++ es nicht,
> Opertoren für Skalare zu überladen.

Die Frage nach dem Passing Mechanismus von Funktionsargumenten stellt 
sich ja nicht nur für Operatoren, sondern für Funktionen ganz allgemein.

Hier muss man in C++ ein wenig mehr mitdenken, als in Java oder C#

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:

>> Die meisten C++-Programmierer sind es nur gewöhnt, skalare Typen per
>> Kopie und Klassen per Referenz zu übergeben, deshalb macht so eine
>> Stelle im Code immer etwas hellhörig.
>
> Anders geht das doch nicht, oder? Jedenfalls erlaubt C++ es nicht,
> Opertoren für Skalare zu überladen.

Den Zusammenhang verstehe ich jetzt nicht. Außerdem kann man durchaus 
auch Skalare als Operanden haben. Es dürfen nur nicht alle Operanden 
Skalare sein.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Läubi .. schrieb:
> Johann L. schrieb:
>> Codeerzeugung von G++ für AVR -- auch wenn's nicht explizit im
>> Thema steht.
> Es steht weder im Titel noch im Text das es um die Codeerzeugung von g++
> für AVR im speziellem geht. Die Frage hat auch für PC Architekturen und
> andere Compiler Gültigkeit, weil dein Problem nicht der avr-g++ ist
> sonder C++ im allgemeinen.

Ja stimmt, es geht nur aus dem angehängten Ausgabe von avr-g++ hervor. 
Codegröße zu diskutieren ist schon was, daß man im Hinblick auf die 
Zielarchitektur machen sollte. War ungeschickt von mit, das nicht 
nochmal zu erwähnen. Auf nem PC würden ein paar Byte mehr oder weniger 
niemanden interessieren, und ich käme auch nicht auf die Idee, mit den 
erzeugten Code anzuschauen.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Johann L. schrieb:
>
>>> Die meisten C++-Programmierer sind es nur gewöhnt, skalare Typen per
>>> Kopie und Klassen per Referenz zu übergeben, deshalb macht so eine
>>> Stelle im Code immer etwas hellhörig.
>>
>> Anders geht das doch nicht, oder? Jedenfalls erlaubt C++ es nicht,
>> Opertoren für Skalare zu überladen.
>
> Den Zusammenhang verstehe ich jetzt nicht. Außerdem kann man durchaus
> auch Skalare als Operanden haben. Es dürfen nur nicht alle Operanden
> Skalare sein.

Das wäre dann aber der Fall, wenn man zB zwei Fixpunkt-Werte 
multiplizieren will. Da muss dann extra eine Klasse her, obwohl die nur 
einen 8-Bit Wert beinhaltet. Und die Funktionen hatte ich so 
hingeschrieben wie ich es auf nem AVR für 8-Bit Werte auch mache, 
nämlich direkt übergeben und nicht per Zeiger (oder Referenz). Für diese 
Multiplikation käme dann eine fmuls-Sequenz als Inline Asm in den 
Operator.

Leider ist C++ keine Obermenge von C (bzw. GNU-C) und es wären sehr viel 
Änderungen in der Quelle notwendig, die nichts mit diesen Operationen zu 
tun haben :-(

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:

> Das wäre dann aber der Fall, wenn man zB zwei Fixpunkt-Werte
> multiplizieren will.

Irgendetwas muss eine konstante Bedeutung haben. Wenn du eine neue 
int-int Rechnerei einführst, woher soll der Compiler dann wissen, wo 
überall diese anzuwenden ist? Soll er zb für Additionen bzw. 
Multiplikationen die im Zusammenhang mit Array Indizierung auftauchen 
ebenfalls deine neue 'Arithmetik' benutzen?

In C++ ist es nun mal das Mittel einer Klasse, sich neue Objekte mit 
neuen Eigenschaften zu bauen.

> Da muss dann extra eine Klasse her, obwohl die nur
> einen 8-Bit Wert beinhaltet.

Macht ja nichts.
Ist ausser ein wenig Schreibarbeit kein großes Ding. Dafür hast du dann 
aber auch einen Datentyp, den du beim Namen nennen kannst.

> Und die Funktionen hatte ich so
> hingeschrieben wie ich es auf nem AVR für 8-Bit Werte auch mache,
> nämlich direkt übergeben und nicht per Zeiger (oder Referenz).

Gewöhn dir für Klassenargumente gleich von vorne herein an: Übergeben 
wird im Normalfall eine Referenz oder eine const Referenz. Damit 
ermöglichst du den Compiler die besten Optimierungsmöglichkeiten. 
Überhaupt im Zusammenspiel mit inline Funktionen.

> Leider ist C++ keine Obermenge von C (bzw. GNU-C) und es wären sehr viel
> Änderungen in der Quelle notwendig, die nichts mit diesen Operationen zu
> tun haben :-(

Huch?
Neue Klasse einführen
Operatoren definieren
Verwenden

Anstelle von uint8_t oder int8_t verwendest du dann deine 
Fixedpoint-Klasse. Soo groß ist der Umstellungsaufwand dann meistens 
auch wieder nicht.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Johann L. schrieb:
>
>> Das wäre dann aber der Fall, wenn man zB zwei Fixpunkt-Werte
>> multiplizieren will.
>
> Irgendetwas muss eine konstante Bedeutung haben. Wenn du eine neue
> int-int Rechnerei einführst, woher soll der Compiler dann wissen, wo
> überall diese anzuwenden ist? Soll er zb für Additionen bzw.
> Multiplikationen die im Zusammenhang mit Array Indizierung auftauchen
> ebenfalls deine neue 'Arithmetik' benutzen?

Momentan habe ich in der C-Quelle ein
typedef int8_t frac8_t;
was allerdings nur dazu dient, beim Durchlesen der Quelle direkt zu 
sehen, wie so ein Ding verwendet wird. (Ok, ging auch ungarisch, mag ich 
aber net.) Immerhin geht ja sowas wie
typedef struct
{
  signed char v;
} S;

S operator+ (S a, S b)
{
  return (S) { a.v + b.v };
} 
und da wär es naheliegend, das auch für nen normalen Typedef zu haben. 
Aber wenn es ohne Overhead möglich ist, Klassen zu verwenden, finde ich 
die Schreibarbeit, die mit Klassenbildung einhergeht, kein Drama.

>> Und die Funktionen hatte ich so
>> hingeschrieben wie ich es auf nem AVR für 8-Bit Werte auch mache,
>> nämlich direkt übergeben und nicht per Zeiger (oder Referenz).
>
> Gewöhn dir für Klassenargumente gleich von vorne herein an: Übergeben
> wird im Normalfall eine Referenz oder eine const Referenz. Damit
> ermöglichst du den Compiler die besten Optimierungsmöglichkeiten.
> Überhaupt im Zusammenspiel mit inline Funktionen.

Bei avr-g++ hab ich noch überhaupt kein Gefühl dafür, wie der tickt und 
wo seine Haken und Ösen sind. Auf nem PC hätt ich da wie gesagt keine 
Bauchschmerzen und der Code der rauskommt ist mir ziemlich wurscht. 
Zumindest kommt's da nicht auf ein paar Bytes mehr oder weniger an.

>> Leider ist C++ keine Obermenge von C (bzw. GNU-C) und es wären sehr viel
>> Änderungen in der Quelle notwendig, die nichts mit diesen Operationen zu
>> tun haben :-(
>
> Huch?
> Neue Klasse einführen
> Operatoren definieren
> Verwenden

Ich meine Sachen wie
#include <avr/pgmspace.h>

enum 
{
    OBJ_1,
    OBJ_2,
    OBJ_3,
    ...
};

typedef struct
{
    int8_t a;
    int8_t b;
    ...
} foo_t;

const foo_t foo[] PROGMEM = 
{
    [OBJ_1] = 
    {
        .a = 1,
        .b = 2,
        ...
    },

    [OBJ_2] = 
    {
        .a = -1,
        .b = 5,
        ...
    },

    [OBJ_3] = 
    ....
};

Die Designators von GNU-C sind zwar nicht notwendig, ich find einen 
Initialiser damit aber wesentlich besser lesbar als einen 
Spaghetti-Initialiser, wo man immer abzählen muss. Wenn man nen recht 
langen Initialiser hat, hat man sich schnell mal verzählt. Ausserdam ist 
es damit bei eine Typänderung wesentlich einfacher, die Initialiser 
konstant zu halten.

Wenn ich's recht sehe, gibt es sowas wie Designators in C++ nicht?

Ausserdem kann man offenbar keine Objekte ins Flash legen und von dort 
aus einlesen. Gibt eine Warnung von avr-g++ auch wenn der erzeugt Code 
ok ist. Ist wohl ein Fall der im AVR-Backend von gcc nicht abgehandelt 
wird. Die Warnung zum Attribut pgmspace kommt ja von dort.

D.h. wenn die Quelle ohne Warnungen übersetzt werden soll, dann muss man 
jede Klasse 2x anlegen: einmal als Klasse und einmal als Struktur, wobei 
letzte ins Flash gelegt werden kann um die Klasse daraus zu 
deserialisieren.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:

> und da wär es naheliegend, das auch für nen normalen Typedef zu haben.

Ein Typedef ist nur ein Alias. Also ein anderer Name. Nach
typedef int my_int;
ist my_int in allen Belangen völlig gleichwertig mit einem int.

Leider.


>> Gewöhn dir für Klassenargumente gleich von vorne herein an: Übergeben
>> wird im Normalfall eine Referenz oder eine const Referenz. Damit
>> ermöglichst du den Compiler die besten Optimierungsmöglichkeiten.
>> Überhaupt im Zusammenspiel mit inline Funktionen.
>
> Bei avr-g++ hab ich noch überhaupt kein Gefühl dafür, wie der tickt und
> wo seine Haken und Ösen sind.

Das hat überhaupt nichts mit dem gcc zu tun, sondern mit C++ an sich. 
Eine Referenz ist ganz einfach nur "ein anderer Name für ein an sonsten 
existierendes Objekt".
Damit darf der Compiler überall dort, wo er eine Referenz hat auch das 
originale Objekt benutzen, wenn er an es rankommt.

> Auf nem PC hätt ich da wie gesagt keine
> Bauchschmerzen

genau so sehen dann auch viele C++ Programme aus und alle Welt schreit: 
Memory Management in C++ ist so schwierig, wir brauchen unbedingt 
Reference-counted garbage collected Klassen.


> Wenn ich's recht sehe, gibt es sowas wie Designators in C++ nicht?

Noch nicht.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Damit darf der Compiler überall dort, wo er eine Referenz hat auch das
> originale Objekt benutzen, wenn er an es rankommt.

Oder genauer: er wird über die Referenz immer das originale Objekt
benutzen; es gibt da keine Wahlfreiheit.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:

> Momentan habe ich in der C-Quelle ein
> typedef int8_t frac8_t;
>
> was allerdings nur dazu dient, beim Durchlesen der Quelle direkt zu
> sehen, wie so ein Ding verwendet wird.

Mehr geht mit einem Typedef auch nicht.

> Immerhin geht ja sowas wie
>
> typedef struct
> {
>   signed char v;
> } S;
>
> S operator+ (S a, S b)
> {
>   return (S) { a.v + b.v };
> }
>
> und da wär es naheliegend, das auch für nen normalen Typedef zu haben.

Das ist auch ein "nomaler Typedef", den du halt nur mit einer 
Klassendefinition verwurstet hast.
Du definierst hier  auf etwas umständliche Art eine Struktur mit Namen 
S, indem du erst eine namenlose Struktur erzeugst und dann über Typedef 
ihr den "alternativen" Namen S gibst und diese beiden Aktionen zu einer 
kombinierst. Es ist also eine verkürzte Schreibeweise von ungefähr dem:
struct Namenlos
{
   signed char v;
};

typedef Namenlos S;

Aber den Typedef könnte man sich auch sparen und stattdessen gleich 
schreiben:
struct S
{
   signed char v;
};

Der Effekt wäre derselbe.
Der Typedef verhält sich hier also auch nicht anders als beim int8_t.
Der Unterschied, weshalb du hier einen Operator definieren kannst, hat 
also nichts damit zu tun, daß der Typedef nicht "normal" wäre, sondern 
damit, daß der im einen Fall auf einen skalaren Typ verweist, im anderen 
auf eine Klasse.

> Aber wenn es ohne Overhead möglich ist, Klassen zu verwenden, finde ich
> die Schreibarbeit, die mit Klassenbildung einhergeht, kein Drama.

Das ist größtenteils durchaus möglich.

> Die Designators von GNU-C sind zwar nicht notwendig, ich find einen
> Initialiser damit aber wesentlich besser lesbar als einen
> Spaghetti-Initialiser, wo man immer abzählen muss. Wenn man nen recht
> langen Initialiser hat, hat man sich schnell mal verzählt.

Zur Not könnte man das auch mit Kommentaren machen.

> Ausserdam ist es damit bei eine Typänderung wesentlich einfacher, die
> Initialiser konstant zu halten.

Warum?

> Wenn ich's recht sehe, gibt es sowas wie Designators in C++ nicht?

Nein.

> Ausserdem kann man offenbar keine Objekte ins Flash legen und von dort
> aus einlesen. Gibt eine Warnung von avr-g++ auch wenn der erzeugt Code
> ok ist.

Was denn für eine?

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Es ist also eine verkürzte Schreibeweise von ungefähr dem:
>
>
> struct Namenlos
> {
>    signed char v;
> };
> 
> typedef Namenlos S;
> 
 typedef struct Namenlos S;

> Aber den Typedef könnte man sich auch sparen und stattdessen gleich
> schreiben:
> 
> [C]
> struct S
> {
>    signed char v;
> };
> 
>
> Der Effekt wäre derselbe.

Nicht, wenn man schreibfaul ist :-)

>> Die Designators von GNU-C sind zwar nicht notwendig, ich find einen
>> Initialiser damit aber wesentlich besser lesbar als einen
>> Spaghetti-Initialiser, wo man immer abzählen muss. Wenn man nen recht
>> langen Initialiser hat, hat man sich schnell mal verzählt.
>
> Zur Not könnte man das auch mit Kommentaren machen.
>
>> Ausserdam ist es damit bei eine Typänderung wesentlich einfacher, die
>> Initialiser konstant zu halten.
>
> Warum?

Typo. Soll heissen

>> Ausserdam ist es damit bei eine Typänderung wesentlich einfacher, die
>> Initialiser konsistent zu halten.
>>             ^^^^^^^^^^

D.h. wenn in der Entwicklungsphase Felder hinzukommen / verschwinden 
etc. Ein Kommentar ist ja kein Teil der Semantik, ein Designator schon.

>> Wenn ich's recht sehe, gibt es sowas wie Designators in C++ nicht?
>
> Nein.
>
>> Ausserdem kann man offenbar keine Objekte ins Flash legen und von dort
>> aus einlesen. Gibt eine Warnung von avr-g++ auch wenn der erzeugt Code
>> ok ist.
>
> Was denn für eine?
class V
{
    public:

        signed char v;

        inline V(){};
        inline V (signed char value) : v(value) {};
};

#include <avr/pgmspace.h>

const uint8_t q[] PROGMEM = 
{
    0x12, 0x11
};

const V w[] PROGMEM = 
{
    V(0x12)
};

V read (void)
{
    return V (pgm_read_byte (&q));
}
warning: only initialized variables can be placed into program memory area
warning: only initialized variables can be placed into program memory area
error: q causes a section type conflict

Und auch ohne w gibt's ne Warnung für q, wennglaich auch keinen 
irreführenden Fehler.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:
>> Der Effekt wäre derselbe.
>
> Nicht, wenn man schreibfaul ist :-)

Stimmt. Das 'typedef' muß man bei deiner Variante zusätzlich tippen. Das 
ist dann aber auch der einzige Unterschied.

> warning: only initialized variables can be placed into program memory area

Ah. Ich schätze, das kommt daher, daß es sich um dynamische 
Initialisierung handelt. Während q nämlich direkt mit einer Konstante 
initialisiert wird, wird das bei w durch einen Funktionsaufruf 
(Konstruktor) gemacht, und (auch wenn's komisch klingt) das würde 
bedeuten, daß der Konstruktor den Flash beschreiben können müßte. In 
diesem Fall könnte das zwar auch wegoptimiert werden, weil der 
Initialisierer ja auch schon im Flash steht und vom Konstruktor nur in 
die Membervariable kopiert wird, aber im allgemeinen Fall geht das eben 
nicht.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Johann L. schrieb:
>>> Der Effekt wäre derselbe.
>>
>> Nicht, wenn man schreibfaul ist :-)
>
> Stimmt. Das 'typedef' muß man bei deiner Variante zusätzlich tippen. Das
> ist dann aber auch der einzige Unterschied.

Nö. Ansonsten muss bei jeder Definition/Deklaration/Cast ein 
struct/union hinzu.

>> warning: only initialized variables can be placed into program memory area
>
> Ah. Ich schätze, das kommt daher, daß es sich um dynamische
> Initialisierung handelt. Während q nämlich direkt mit einer Konstante
> initialisiert wird, wird das bei w durch einen Funktionsaufruf

Die Warnung kommt auch bei
class V
{
    public:
        signed char v;
        inline V(){};
        inline V (signed char value) : v(value) {};
};

#include <avr/pgmspace.h>

const uint8_t q[] PROGMEM = 
{
    0x12, 0x11
};

V read (void)
{
    return V (pgm_read_byte (&q));
}

> (Konstruktor) gemacht, und (auch wenn's komisch klingt) das würde
> bedeuten, daß der Konstruktor den Flash beschreiben können müßte. In
> diesem Fall könnte das zwar auch wegoptimiert werden, weil der
> Initialisierer ja auch schon im Flash steht und vom Konstruktor nur in
> die Membervariable kopiert wird, aber im allgemeinen Fall geht das eben
> nicht.

Mit dem Initializer für w (der V() verwendet) kommt ja auch wie erwartet 
ein Fehler, aber der beschwert sich über q !

Autor: Andreas Ferber (aferber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:
>> Stimmt. Das 'typedef' muß man bei deiner Variante zusätzlich tippen. Das
>> ist dann aber auch der einzige Unterschied.
> Nö. Ansonsten muss bei jeder Definition/Deklaration/Cast ein
> struct/union hinzu.

In C++? Nein. "struct foo;" deklariert in C++ einen Klassen-Typ namens 
"foo", der ohne "struct" davor verwendet werden kann. Dasselbe gilt auch 
für "union". Im Gegenteil, es ist eine explizite Ausnahme im Standard 
nötig, damit ein "typedef struct foo foo;" zu keinem Fehler führt (die 
Ausnahme ist wohl im wesentlichen der Verwendbarkeit von C-Headern 
geschuldet). Ein "struct foo; typedef int foo;" liefert in C++ einen 
Fehler, während es in C erlaubt wäre.

Andreas

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:
> Rolf Magnus schrieb:
>> Johann L. schrieb:
>>>> Der Effekt wäre derselbe.
>>>
>>> Nicht, wenn man schreibfaul ist :-)
>>
>> Stimmt. Das 'typedef' muß man bei deiner Variante zusätzlich tippen. Das
>> ist dann aber auch der einzige Unterschied.
>
> Nö. Ansonsten muss bei jeder Definition/Deklaration/Cast ein
> struct/union hinzu.

Ähm. nein.

In C: ja
In C++: nein

Autor: Andreas Ferber (aferber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:
>> Ah. Ich schätze, das kommt daher, daß es sich um dynamische
>> Initialisierung handelt. Während q nämlich direkt mit einer Konstante
>> initialisiert wird, wird das bei w durch einen Funktionsaufruf
> Die Warnung kommt auch bei

Das dürfte BTW eine Instanz dieses GCC-Bugs sein:

http://www.avrfreaks.net/index.php?name=PNphpBB2&f...
Beitrag "avr-gcc, C++ und PROGMEM"

Andreas

Autor: High Performer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Rolf Magnus:

>Er verändert keinen Operanden, auch wenn das ein Java-Programmierer
>nicht erwarten würde, weil der den Unterschied zwischen call-by-value
>und call-by-reference nicht kennt. ;-)

Ich bin mit C++ groß geworden und erst später in Java eingestiegen. ;-) 
Allerdings sehe ich in:

>static inline V operator+ (V a, const V b)
>{
>    a.v += b.v;
>    return a;
>}

keine Referenz, und außerdem bleibe ich dabei: die Methode verändert a, 
was laut Kontrakt des Additionsoperators nicht sein sollte. Außerdem 
liefert er dann (das veränderte) a als Ergebnis, was ebenfalls nicht 
sein sollte.
Oder habe ich irgend etwas Wichtiges übersehen?

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
High Performer schrieb:
> @Rolf Magnus:
>
>>Er verändert keinen Operanden, auch wenn das ein Java-Programmierer
>>nicht erwarten würde, weil der den Unterschied zwischen call-by-value
>>und call-by-reference nicht kennt. ;-)
>
> Ich bin mit C++ groß geworden und erst später in Java eingestiegen. ;-)
> Allerdings sehe ich in:
>
>>static inline V operator+ (V a, const V b)
>>{
>>    a.v += b.v;
>>    return a;
>>}
>
> keine Referenz,

Ja, eben. Genau deshalb kann der Operator auch den Operanden gar nicht 
verändern. Er wurde "by value" übergeben und nicht per Referenz. Der 
Operator verändert lediglich eine lokale Kopie. Man könnte das natürlich 
auch selber machen, indem man den Parameter als Referenz definiert und 
dann innerhalb der Funktion selbst eine Kopie anlegt, aber das ist 
umständlicher und hat keinen Vorteil.

> und außerdem bleibe ich dabei: die Methode verändert a,

Sie verändert a, aber a ist nicht der übergebene Operand, sondern eine 
Kopie davon.

> was laut Kontrakt des Additionsoperators nicht sein sollte.

Wenn er nicht veränderbar sein soll, mußt du ihn als const definieren. 
Aber außerhalb der Funktion hätte das keinerlei Auswirkung.

Autor: High Performer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Rolf:

Du hast natürlich vollkommen Recht! Ich sah mal wieder den Wald vor 
lauter Bäumen nicht. Hänge gerade wohl doch ein wenig zu sehr in Java 
drin. ;-)

Mea culpa!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.