Forum: Compiler & IDEs Concatenation und anschließende Zuweisung eines Wertes


von Micha (Gast)


Lesenswert?

Hallo zusammen,
ich suche nach einer Möglichkeit mittels Concat einen Makronamen 
zusammenzusetzen und diesem dann einen Wert zuzuweisen. Also etwa wie 
folgt:
1
#define CONCAT(X,Y) X##_##Y
2
#define TEMPLATE(X,Y) CONCAT(X,Y)
3
#define PREFIX test
4
5
// gewünscht wäre ein Makro namens test_SYMBOL mit dem Wert 8
6
#define TEMPLATE(PREFIX,SYMBOL)    (8)
Die letzte Zeile wird mit folgender Warnung quittiert:
incompatible redefinition of macro "TEMPLATE"

Ist ja auch klar, aber was muss ich tun um das umzusetzen oder geht das 
gar nicht?

von Karl H. (kbuchegg)


Lesenswert?

Das geht nicht.

Dazu müsste der Präprozessor sich selbst eine Makrodefinition erzeugen, 
in der dann der Name des Makros im Laufe des Prozesses entsteht. Das ist 
aber ausserhalb der Möglichkeiten des Präprozessors.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Aber wenn es nicht unbedingt ein beliebiger neuer Makro sein muss,
der da rauskommt, kann man so einen Ansatz schon verfolgen.  Typisches
Beispiel ist bei einem AVR, aus dem Portbuchstaben („B“) die drei
Namen für die Port-Register („PORTB“, „DDRB“ und „PINB“) zu zimmern.

von Peter D. (peda)


Lesenswert?

Jörg Wunsch schrieb:
> Typisches
> Beispiel ist bei einem AVR, aus dem Portbuchstaben („B“) die drei
> Namen für die Port-Register („PORTB“, „DDRB“ und „PINB“) zu zimmern.

Wie soll das gehen?

Bei einem Macro kann nur die rechte Seite Macros beinhalten, niemals die 
linke Seite.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
>> Typisches
>> Beispiel ist bei einem AVR, aus dem Portbuchstaben („B“) die drei
>> Namen für die Port-Register („PORTB“, „DDRB“ und „PINB“) zu zimmern.
>
> Wie soll das gehen?

Beitrag "Re: Linux und (immer noch) C"

von Karl H. (kbuchegg)


Lesenswert?

Jörg Wunsch schrieb:
> Peter Dannegger schrieb:
>>> Typisches
>>> Beispiel ist bei einem AVR, aus dem Portbuchstaben („B“) die drei
>>> Namen für die Port-Register („PORTB“, „DDRB“ und „PINB“) zu zimmern.
>>
>> Wie soll das gehen?
>
> Beitrag "Re: Linux und (immer noch) C"

Ist allerdings nicht ganz vergleichbar.

Was der TO in erster Linie machen will, das ist dass er hier
1
#define Makro   Ersetzung
2
        *****
den Namen des Makros per anderen Makros zusammenbauen lässt.
Was so erst mal nicht geht.

Also muss er einen Plan B ins Auge fassen. Der könnte so aussehen, wie 
du das gezeigt hast, dass sich ein Makro aus einem anderen Makro den 
Teil aus dem Ersetzungstext rausholt den es braucht.

Aber ein
1
#define PREFIX  cfg
2
#define SYMBOL  Offset
3
    
4
#define TEMPLATE(PREFIX,SYMBOL)    (8)

welches dann dafür sorgt, dass damit eine gleichwertige Funktionalität 
zu
1
#define cfg_Offset  (8)
erreicht wird, ist so nicht möglich. Ein derartiges hypothetisches Makro 
'TEMPLATE' ist mit dem Präprozessor nicht formulierbar.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz schrieb:
> Ist allerdings nicht ganz vergleichbar.

Ja.  Dass das, was er da als Wunschvorstellung formuliert hat, so
nicht machbar ist, ist schon klar.  Aber vielleicht lässt sich ja
das, was er dann mit den neu definierte Makros anstellen will,
dennoch auf so einem Weg erreichen.

von Micha (Gast)


Lesenswert?

Ich würde mit dem Ergebnis gerne die Anzahl Elemente eines Arrays 
festlegen, also:
1
uint8_t test_array[test_SYMBOL];
Dieses Array ist Teil einer Struktur, die innerhalb einer header-Datei 
deklariert ist um sie in anderen Modulen verwenden zu können.

Ich könnte auch mit
1
static const uint16_t TEMPLATE(PREFIX,SYMBOL) = 8;
2
//...
3
struct TEMPLATE(PREFIX,FIFO) {
4
   uint8_t read;
5
   uint8_t write;
6
   struct xxx entry[TEMPLATE(PREFIX,SYMBOL)];
7
};
leben, aber das scheint mir in einer header-Datei deplatziert zu sein.

Letztlich möchte ich http://arnold.uthar.net/index.php?n=Work.TemplatesC 
mit dem hiesigen FIFO-Code 
(http://www.mikrocontroller.net/articles/FIFO#2n-Ringpuffer_-_die_schnellste_L.C3.B6sung) 
kombinieren, da ich gelegentlich FIFOs mit unterschiedlich arrangierten 
Daten nutze und den Code möglichst nicht kopieren möchte.

von Eric B. (beric)


Lesenswert?

Hm, eine Option wäre der ganze struct definition im Makro zu packen:
1
#define TYPEDEF_FIFO(prefix, size, payload) \
2
typedef struct { \
3
  int read; \
4
  int write; \
5
  struct payload data[size]; \
6
} prefix ## _fifo_str;
7
8
TYPEDEF_FIFO(test, 8, { int value; char * text; })
9
10
test_fifo_str test_fifo;
11
12
int main()
13
{
14
   test_fifo.data[0].value = 75;
15
}

Edit: wobei dann eigentlich für alle mögliche Zugriffe auf den FIFO 
Makros definiert werden sollen z.B.
1
FIFO(prefix) prefix ## _fifo_str
2
3
FIFO(test) test_fifo; /* anstatt direkt "test_fifo_str test_fifo" wie oben */

: Bearbeitet durch User
von Micha (Gast)


Lesenswert?

Eric B. schrieb:
> Edit: wobei dann eigentlich für alle mögliche Zugriffe auf den FIFO
> Makros definiert werden sollen z.B.
Das ist nicht das Problem - das funktioniert bereits alles. Der einzige 
kleine Schönheitsfehler an der Sache ist momentan, dass alle FIFOs 
dieselbe Anzahl an Elementen haben (müssen).

von Micca (Gast)


Lesenswert?

Du könntest die Arraygröße statt über #define per enum definieren, dann 
müsste es gehen:
1
enum {TEMPLATE(PREFIX,SYMBOL) = 8};
2
3
//...
4
struct TEMPLATE(PREFIX,FIFO) {
5
   uint8_t read;
6
   uint8_t write;
7
   struct xxx entry[TEMPLATE(PREFIX,SYMBOL)];
8
};

von Peter D. (peda)


Lesenswert?

Micha schrieb:
> Der einzige
> kleine Schönheitsfehler an der Sache ist momentan, dass alle FIFOs
> dieselbe Anzahl an Elementen haben (müssen).

Du kannst doch auch die Größe den Macros als Argument übergeben bzw. in 
den Funktionen mit sizeof() ermitteln.

von Micca (Gast)


Lesenswert?

Nachtrag

Die Lösung wäre mir trotzdem zu frickelig.

Frage: warum brauchst du überhaupt die Größe als Konstante? Solange du 
nicht über irgendwelche (void *) auf die Fifo zugreifts, kannst Du doch 
über sizeof(entry)/sizeof(entry[0]) die Größe immer ermitteln.

Oder packe die Size einfach mit in die struct.
Braucht 2 Byte mehr, aber macht das ganze transparenter (Init der Size 
nicht vergessen...)

Irgendwo hatt ich genau deinen Ansatz mit diesem Konstrukt…Mmmm

von Micha (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Du kannst doch auch die Größe den Macros als Argument übergeben bzw. in
> den Funktionen mit sizeof() ermitteln.

Micca schrieb:
> Frage: warum brauchst du überhaupt die Größe als Konstante?

Um die Anzahl der Elemente zu definieren, sprich wie viel Platz das FIFO 
bietet. Das heißt wie viele Arrays oder Strukturen passen in das FIFO? 
Die Größe eines dieser Elemente bestimme ich tatsächlich mit sizeof.

Der Ansatz mit dem enum funktioniert:
1
// concat macros
2
#define CONCAT(X,Y) X##_##Y
3
#define TEMPLATE(X,Y) CONCAT(X,Y)
4
5
// fifo definition section
6
#define FIFO_PREFIX  test
7
enum {TEMPLATE(FIFO_PREFIX,FIFO_SIZE) = 8};
8
9
typedef struct TEMPLATE(FIFO_PREFIX,FIFO_ENTRY) {
10
   uint8_t    reg;
11
   uint16_t   data;
12
} TEMPLATE(FIFO_PREFIX,fifo_entry_t);
13
14
// generic section
15
typedef struct TEMPLATE(FIFO_PREFIX,FIFO) {
16
   uint8_t    read;
17
   uint8_t    write;
18
   TEMPLATE(FIFO_PREFIX,fifo_entry_t)  entry[TEMPLATE(FIFO_PREFIX,FIFO_SIZE)];
19
} TEMPLATE(FIFO_PREFIX,fifo_t);
führt dann zu
1
enum {test_FIFO_SIZE = 8};
2
3
typedef struct test_FIFO_ENTRY {
4
   uint8_t    reg;
5
   uint16_t   data;
6
} test_fifo_entry_t;
7
8
// generic section
9
typedef struct test_FIFO {
10
   uint8_t    read;
11
   uint8_t    write;
12
   test_fifo_entry_t  entry[test_FIFO_SIZE];
13
} test_fifo_t;

von (prx) A. K. (prx)


Lesenswert?

Es gibt ja ein paar Leute hier im Forum, die C++ auf Microcontrollern 
als Katastrophe ansehen. Würde mich interessieren, ob sie dieses Gewürge 
hier für sinnvoller halten.

von Micha (Gast)


Lesenswert?

Naja eine Augenweide ist es nicht unbedingt, das stimmt schon. Aber ich 
könnte mir durchaus auch Schlimmeres vorstellen...

Als jemand, der keinerlei C++ Erfahrung hat: wäre es denn möglich eine 
entsprechende Klasse mit Templates zu erstellen und diese dann in einem 
"normalen" C-Projekt zu verwenden?

von Micca (Gast)


Lesenswert?

Moin, ich programmiere embedded Systeme sowohl in C als auch in C++ , 
kurz meine 5 Cent…

> Als jemand, der keinerlei C++ Erfahrung hat: wäre es denn möglich
> eine entsprechende Klasse mit Templates zu erstellen und diese
> dann in einem "normalen" C-Projekt zu verwenden?

Ja, das ist möglich, aber warum? Es tut was es soll.
Du hast die Auswahl der Programmiersprache gemacht, weil Du dich damit 
auskennst. Du hattest das Problem viele unterschiedliche FIFOs zu 
benötigen und hast eine Lösung dafür gefunden. (BTW: wie viele sind es 
denn tatsächlich…?)

Statt dann einen C++ Wrapper auf C zu bauen hätte ich es gleich komplett 
in C++ erstellt (unabhängig davon ob Klassen genutzt werden oder nicht).



Ich empfehle gerne die C++ FAQ: „Is C++ better than…?“
http://isocpp.org/wiki/faq/big-picture#imperfect-language
Bzw „Is it important to know the technical definition of “good OO”? Of 
“good class design”? ¶ ?
http://isocpp.org/wiki/faq/big-picture#biz-dominates-tech

Ich kann mit der Lösung leben, würde sie aber nicht in einem großen 
kommerziellen Projekt nutzen, wenn die restlichen Programmierer dieses 
Konstrukt nicht kennen und anwenden müssten. Da wären mir die Risiken 
für Fehler zu groß (und Fehlersuche kostet Zeit, also Geld).


Ein Nachteil in meinen Augen ist Includierung von C Dateien in C 
Dateien. Auch die lose Kopplung zw. Array und enum (test_FIFO, 
test_FIFO_SIZE) gefällt mir nicht. Aber was soll’s, es funktioniert.

von Micca (Gast)


Lesenswert?

Hallo Micha
(BTW: hoffe dass die Namensgleichheit nicht alle Mitleser verwirrt).

Eine Frage ist mir noch eingefallen:

C-Makros kann man meist nicht gut debuggen. Die Toolchains können damit 
nicht umgehen.
Wie ist das in Deinen Template() Funktionen? Kann man hineinspringen 
(Step-Into) und in Einzelschritten (Single Step) durchsteppen? 
Breakpunkt setzen geht vermutlich nicht. Korrekt?

von Hans (Gast)


Lesenswert?

Micha schrieb:
> Als jemand, der keinerlei C++ Erfahrung hat: wäre es denn möglich eine
> entsprechende Klasse mit Templates zu erstellen und diese dann in einem
> "normalen" C-Projekt zu verwenden?

99% des normalen C-Codes lässt sich ohne Änderungen mit einem 
C++-Compiler übersetzen. Die restlichen 1% (fehlende Casts bei void* 
etc.) sind schnell korrigiert. Ausnahme sind ggf. 
compiler/plattformpezifische Konstrukte.

Sofern es für Deine Plattform einen C++-Compiler gibt, könntest Du das 
Projekt also recht einfach auf den C++-Compiler umstellen. Ab dann 
kannst Du im bestehdenen Code Templates, Klassen usw. verwenden.

Wie viel C++-Features Du benutzt, liegt ganz an Dir. Man kann auch mit 
C++ reines C programmieren.

von Micha (Gast)


Lesenswert?

Micca schrieb:
> BTW: wie viele sind es denn tatsächlich…?
Ehrlich gesagt hätte ich die FIFOs locker noch von Hand pflegen können, 
sprich die Dateien kopieren und die Namen umbenennen. Aber mir gefiel 
nicht, dass ich etwaige Änderungen dann in allen Kopien machen und bei 
jeder neuen Verwendung alle Namen ändern muss. Und generische Namen 
fördern die Übersicht imo nicht unbedingt. Ich sehe es daher als eine 
Art Machbarkeitsstudie.

Micca schrieb:
> Auch die lose Kopplung zw. Array und enum (test_FIFO,
> test_FIFO_SIZE) gefällt mir nicht.
Das ist aber mehr oder weniger ein generelles Problem mit enums in C, 
nicht?

Micca schrieb:
> Wie ist das in Deinen Template() Funktionen? Kann man hineinspringen
> (Step-Into) und in Einzelschritten (Single Step) durchsteppen?
Ja, das geht. Unschön dabei ist jedoch, dass nicht der tatsächliche 
aktuelle Name angezeigt wird, sondern der zuerst definierte. Man muss 
also wissen in welcher Funktion man sich befindet.

Da ich aber bspw. "test1_FifoRead()" aufrufe, kann ich damit leben. D.h. 
in meinem Code nutze ich nicht mehr die Templates, sondern die Namen, 
die der Compiler daraus gemacht hat.

IDE ist CodeComposerStudio 5.5 bzw. Eclipse Luna für STM32 aufbereitet.

Micca schrieb:
> Breakpunkt setzen geht vermutlich nicht. Korrekt?
Das geht bedingt. Je nach Position des BP müssen alle anderen erstmal 
deaktiviert werden. Und es sieht so aus als würde ein BP in jeder der 
generierten Funktionen gesetzt, da das Debugging immer dort stoppt, egal 
ob "test1_FifoRead()" oder "test2_FifoRead()" aufgerufen wurde. Die 
Sache mit den Namen von oben gilt natürlich ebenfalls.

Wie steht es hinsichtlich dieser beiden Punkte bei Benutzung von C++ 
Templates? Kann ein Breakpoint dort speziell in einer einzelnen 
Implementierung (oder wie auch immer das dann heißt) gesetzt werden? 
Sagen wir mal bei Benutzung o.g. IDE und dem GCC.

Hans schrieb:
> Wie viel C++-Features Du benutzt, liegt ganz an Dir. Man kann auch mit
> C++ reines C programmieren.
Zukünftig werde ich definitiv darüber nachdenken. Ich denke auch der 
Overhead ist zu verschmerzen und hält sich in Grenzen wenn man auf ein 
paar Dinge achtet.

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.