Forum: Compiler & IDEs Wieviel Präprozessorei ist sinnvoll?


von Micha (Gast)


Lesenswert?

Ein Merkmal von guten Quelltexten ist deren Lesbarkeit bzw. 
Nachvollziehbarkeit. Bei meinen ersten Gehversuchen mit C (AVR 
Prozessoren) sah dann beispielsweise das Ein- uns Ausschalten einer LED> 
etwa so aus:
1
  PORTB |= 0b00000010;   // LED an
2
  ...
3
  PORTB &= ~0b00000010;  // LED aus
Zugegebenermaßen gruselig, hat mir auch einiges Nachdenken gekostet, als 
ich aus Versehen statt 0b mal 0x vorne dran stehen hatte - gab "nur" 
eine Warnung, ging aber trotzdem nicht. Das sind so Fehler die man nur 
einmal macht ;-)
Schaut man sich Codebeispiele von anderen an, sieht es oft so in der Art 
aus:
1
#define LED  PB1
2
...
3
  PORTB |= 1<<LED;   // LED ein
4
  ...
5
  PORTB &= ~(1<<LED); // LED aus
Besser? Zumindest etwas besser wartbar. Für manche immer noch kryptisch, 
wenn man die Grundregeln der Bit-Manipuliererei einmal verstanden hat 
aber ganz ok.
Manche gehen einen Schritt weiter und schreiben sowas in der Art:
1
#define LED_ON  PORTB |= 1<<PB1;
2
#define LED_OFF PORTB &= ~(1<<PB1);
3
...
4
  LED_ON;
5
  ...
6
  LED_OFF;
Vielleicht am besten lesbar, aber eigentlich kein "richtiges" C mehr(?) 
Hab jedenfalls den Eindruck bei sowas scheiden sich die Geister.
Welche Art, solche Sachen zu coden hatet ihr für optimal bzgl. 
Lesbarkeit und Wartbarkeit - eher sparsamen Einsatz des Präprozessors 
oder extensiven Gebrauch dieser Möglichkeit?

von C-Help (Gast)


Lesenswert?

Ich verwende gerne:
1
#define LED_ON()    PORTB |= 1<<PB1
2
#define LED_OFF()   PORTB &= ~(1<<PB1)
3
...
4
   LED_ON();
5
   ...
6
   LED_OFF();

So sieht es wieder nach C aus.
Wenn man es ganz richtig machen will, kann man noch ein do{} while (0) 
drum setzen.

von Peter D. (peda)


Lesenswert?

Micha schrieb:
> Das sind so Fehler die man nur
> einmal macht ;-)

Sicher?
Man kann sich da gerne mal um eine Bitstelle verzählen.

Man kann Ports aber auch als Struct von Bitvariablen definieren.
Das finde ich am besten lesbar.

Beitrag "Re: Problem mit Befehl SET"

von Falk B. (falk)


Lesenswert?

@ Micha (Gast)

>  LED_ON;
>  ...
>  LED_OFF;

>Vielleicht am besten lesbar,

Eben!

>aber eigentlich kein "richtiges" C mehr(?)

Doch!

>Hab jedenfalls den Eindruck bei sowas scheiden sich die Geister.

Das tun sie sich immer.

>Welche Art, solche Sachen zu coden hatet ihr für optimal bzgl.
>Lesbarkeit und Wartbarkeit

Siehe oben! Bei der Programierung will ich bestimmte Aktionen auslösen, 
wie z.B. die LED schalten. Mit welchem Pin das passiert, ist mir dann 
egal! UNd ich ich will es auch nicht dauernd hinschreiben müssen!
Vor allem, wenn man die Pinbelegung mal ändern will (Projektänderung, 
neues Projekt basierend auf bestehendem Code)

> - eher sparsamen Einsatz des Präprozessors

Nein.

>oder extensiven Gebrauch dieser Möglichkeit?

Man sollte eine gesunde Mischung anstreben. Extreme sind selten gut.

von Axel S. (a-za-z0-9)


Lesenswert?

Micha schrieb:

[schnipp]

> Manche gehen einen Schritt weiter und schreiben sowas in der Art:
>
1
> #define LED_ON  PORTB |= 1<<PB1;
2
> #define LED_OFF PORTB &= ~(1<<PB1);
3
> ...
4
>   LED_ON;
5
>   ...
6
>   LED_OFF;
7
>
> Vielleicht am besten lesbar, aber eigentlich kein "richtiges" C mehr(?)
> Hab jedenfalls den Eindruck bei sowas scheiden sich die Geister.
> Welche Art, solche Sachen zu coden hatet ihr für optimal bzgl.
> Lesbarkeit und Wartbarkeit - eher sparsamen Einsatz des Präprozessors
> oder extensiven Gebrauch dieser Möglichkeit?

Die Frage ist falsch gestellt.

Das Ziel sollte immer beste Lesbarkeit des Codes sein. Weil das i.d.R. 
gleichzeitig auch die beste Wartbarkeit, die beste Nachnutzbarkeit und 
die geringste Fehleranfälligkeit bedeutet.

Ob dieses Ziel durch den Einsatz des Präprozessors erreicht wird, durch 
die Definition entsprechender Funktionen in der verwendeten Hochsprache
1
inline void LED_on(void) {
2
  PORTB |= 1<<PB1;
3
}
4
...
5
LED_on();

oder noch ganz anders, ist dabei Nebensache. Außer wenn die verwendeten 
Sprachmittel selber wiederum die Lesbarkeit beeinträchtigen.

In C verwendet mal halt traditionell eher den Präprozessor, weil da auch 
mit Uralt-Compilern schneller Code bei rauskommt. Aktuelle Compiler 
können Inlining von Wrapper-Funktionen wie oben automatisch. An der 
Stelle will man dann vielleicht lieber die Funktion nutzen.

Persönlich glaube ich, daß Aussagen der Art "der Präprozessor ist pöhse" 
hauptsächlich von Leuten kommen, die den Präprozessor nicht verstehen 
und (deshalb) Angst davor haben.


XL

von Oliver (Gast)


Lesenswert?

So viel wie nötig, so wenig wie möglich...

Was ist denn an der Schreibweise hier unverständlich?
1
LED_PORT = (1 << LED0) | (1 << LED2) | (1 << LED3) | (1 << LED5);

Oliver

von Schaulus Tiger (Gast)


Lesenswert?

Unverständlich ist, warum jemand freiwillig so viele "<<" und "()" 
eintippt. Warum nicht einfach
1
LED_PORT = LED0 | LED2 | LED3 | LED5;

von Coder (Gast)


Lesenswert?

oder sowas
1
#define LED_PORT   PORTB
2
3
#define LED_bm   ((uint8_t)(1U << 1U)
4
5
6
static void LedOn(void);
7
8
static void LedOn(void)
9
{
10
 LED_PORT|=LED_bm;
11
}

von (prx) A. K. (prx)


Lesenswert?

Schaulus Tiger schrieb:
> Unverständlich ist, warum jemand freiwillig so viele "<<" und "()"
> eintippt.

Spätestens wenn du Bits nicht als Maske, sondern als Bitnummer brauchst, 
bist du froh drüber, wenn die I/O-Pins als Bits und nicht als Maske 
definiert wurden. Denn vom Bit zur Maske ist erheblich einfacher als 
umgekehrt (Hausaufgabe: wie geht das ohne Laufzeitrechnung ;-).

Eine Stelle, wo man Bits benötigt, ist beispielsweise die 
Bitadressierung bei einigen Cortex-M Cores.

von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:
> Schaulus Tiger schrieb:
>> Unverständlich ist, warum jemand freiwillig so viele "<<" und "()"
>> eintippt.
>
> Spätestens wenn du Bits nicht als Maske, sondern als Bitnummer brauchst,
> bist du froh drüber, wenn die I/O-Pins als Bits und nicht als Maske
> definiert wurden.

Ich denke, die Anmerkung war ein Plädoyer dafür, die angeborene Faulheit 
aller Programmierer auch in dem Sinne auszuleben, dass man die << und () 
genauso in Makros versteckt. War A sagt, sollte nicht bei B stehen 
bleiben und auch C sagen.
Wogegen prinzipiell ja nichts zu sagen ist, solange der Punkt nicht 
überschritten wird, an dem man dann schon wieder zu viel in Makros aus 
dem eigentlichen Code verbannt und den gegenteiligen Effekt erzielt. 
Dann wird der Makro-verwendende Code nicht mehr einfacher (im Sinne von 
einfacher zu lesen), sondern nur noch kryptischer.

von Thomas E. (thomase)


Lesenswert?

Micha schrieb:
> oder extensiven Gebrauch dieser Möglichkeit?

Wenn ich F_CPU ändere, werden sämtlich Einstellungen, die davon abhängig 
sind, automatisch übernommen.

Z.B. so:
1
#if (F_CPU / 2) < 200000
2
  #define ADC_DEFAULT_PRESCALER (1 << ADPS0)
3
#elif (F_CPU / 4) < 200000
4
  #define ADC_DEFAULT_PRESCALER (1 << ADPS1)
5
#elif (F_CPU / 8) < 200000
6
  #define ADC_DEFAULT_PRESCALER ((1 << ADPS1) | (1 << ADPS0))
7
#elif (F_CPU / 16) < 200000
8
  #define ADC_DEFAULT_PRESCALER (1 << ADPS2)
9
#elif (F_CPU / 32) < 200000
10
  #define ADC_DEFAULT_PRESCALER ((1 << ADPS2) | (1 << ADPS0))
11
#elif (F_CPU / 64) < 200000
12
  #define ADC_DEFAULT_PRESCALER ((1 << ADPS2) | (1 << ADPS1))
13
#elif (F_CPU / 128) < 200000
14
  #define ADC_DEFAULT_PRESCALER ((1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0))
15
#endif

mfg.

von (prx) A. K. (prx)


Lesenswert?

Allerdings besteht gerade in diesem Fall kein Zwang, das mit dem 
Präprozessor zu machen. Der Compiler sortiert konstante Bedingungen auch 
selber aus.

von Bernd K. (prof7bit)


Lesenswert?

Diese Variante hier ist ganz witzig (man beachte die sehr kurze 
Definition von Port und Pin in einer Zeile):
1
#define LED        B,5
2
#define SENSOR     B,4
3
4
#define _SET(type,name,bit)      type##name |= _BV(bit)
5
#define _CLR(type,name,bit)      type##name &= ~_BV(bit)
6
#define _GET(type,name,bit)      (type##name & _BV(bit))
7
#define SET_AS_OUTPUT(pin)       _SET(DDR,pin)
8
#define SET_AS_INPUT(pin)        _CLR(DDR,pin)
9
#define SET_HIGH(pin)            _SET(PORT,pin)
10
#define SET_LOW(pin)             _CLR(PORT,pin)
11
#define TOGGLE(pin)              _SET(PIN,pin)
12
#define IS_HIGH(pin)             _GET(PIN,pin)
13
14
15
16
int main() {
17
  SET_AS_OUTPUT(LED);
18
  SET_HIGH(LED);
19
  if (IS_HIGH(SENSOR)) {
20
    ....

: Bearbeitet durch User
von Thomas E. (thomase)


Lesenswert?

A. K. schrieb:
> Der Compiler sortiert konstante Bedingungen auch
> selber aus.

Hab ich mir jetzt schon dreimal durchgelesen und immer noch nicht 
verstanden.

mfg.

von (prx) A. K. (prx)


Lesenswert?

Thomas Eckmann schrieb:
> Hab ich mir jetzt schon dreimal durchgelesen und immer noch nicht
> verstanden.

Ein guter Optimizer sorgt dafür, dass bei
1
if ((F_CPU / 2) < 200000)
2
  ... (1 << ADPS0) ...
3
else if ((F_CPU / 4) < 200000)
4
  ... (1 << ADPS1) ...
vom Aufwand her circa das Gleiche rauskommt wie bei dir, selbst wenn du 
das in einer Funktion vergräbst, die er dann inlinen darf. Die 
Bedingungen sind ja konstant, weshalb nur einer der Zweige überhaupt zu 
Code gerinnt.

: Bearbeitet durch User
von Thomas E. (thomase)


Lesenswert?

A. K. schrieb:
> vom Aufwand her circa das Gleiche rauskommt wie bei dir, selbst wenn du
> das in einer Funktion vergräbst, die er dann inlinen darf. Die
> Bedingungen sind ja konstant, weshalb nur einer der Zweige überhaupt zu
> Code gerinnt.

Das hellt die Sache ein wenig auf. Kann man auch so machen und der 
Compiler wird da auch mitspielen.

Aber unter dem Satz zuvor konnte ich mir absolut nichts vorstellen.

mfg.

von (prx) A. K. (prx)


Lesenswert?

Thomas Eckmann schrieb:
> Aber unter dem Satz zuvor konnte ich mir absolut nichts vorstellen.

Es gibt Aufgaben, die man gleichermassen mit Präprozessor wie mit dem 
Compiler selbst erledigen kann, oder es eigentlich können sollte. Dein 
Beispiel gehört dazu.

Und es gibt welche, wo das nicht geht, weil der Compiler dabei auf die 
Nase fällt. Wenn man mit dem Präprozessor beispielsweise Spezifika der 
verwendeten Umgebung unterscheidet, also Compiler, Zielmaschine etc. 
Denn da ist gewöhnlich nur einer der Zweige überhaupt compilierbar, die 
anderen laufen auf Syntaxfehler, fehlende Definitionen aus fremden 
Includes etc.

Stroustrup, kein grosser Freund des Präprozessors, hatte sich in C++ 
etwas Gedanken darum gemacht, wie man den Präprozessor aus der ersten 
Kategorie verbannen kann. Was zu echten Konstanten führte, weshalb also 
N von
  const int N = 10;
in C++ oft an Stellen verwendbar ist, die lexikalische Konstanten 
erwarten, während das in C nicht geht.

: Bearbeitet durch User
von Roland P. (pram)


Lesenswert?

Es ist zwar schon fast OT, aher hier ein Beispiel wie man es wirklich 
übertreiben kann (Quelle: http://www.ioccc.org/2005/chia/)
1
/*
2
 * Sun's Java is often touted as being "portable", even though my code won't
3
 * suddenly become uber-portable if it's in Java. Truth is, Java's one of
4
 * the most ugly, slow, and straitjacketed languages ever. It's popular
5
 * mainly because people hear the word "portable" and go "ewww".
6
 *
7
 * This program, then, is dedicated to bringing about the death of Java. We
8
 * good coders have been oppressed for too long by the lame language
9
 * decisions of pointy-haired bosses and academics who should know better. 
10
 * It's time we stand up against this junk, and bring back the fun in
11
 * programming! Viva La Revolution!
12
 */
13
14
#define aSet c
15
#define BufferedReader(x)1
16
#define byte Y[I][_^1]?do(:):_&1?do(.):do(`):8;++y;}
17
#define class int N=0,_,O=328,l=192,y=4,Y[80][64]={0},I;struct
18
#define do(c)a(#c "\b")
19
#define err c,c
20
#define getAllStrings(x));q()
21
#define if(x)b(#x)
22
#define IOException
23
#define line c
24
#define main(a)b(char*x){write(1,"\033[",2),null}main()
25
#define new
26
#define null a(x);}a(char*x){write(1,x,strlen(x));try;try;try;try;
27
#define out c,c
28
#define println(x)c
29
#define private int d(int
30
#define public short c;}c;typedef int BufferedReader;char*F="JF>:>FB;;BII";
31
#define return {return
32
#define static f(x){N=(N+x)%6,y--?f(0),f(1),f(4),f(1):++Y[(I=O+N[F]-66)
33
#define String
34
#define System c
35
#define this if(D):1,O=I,I/=16,l<_/32?if(B):l>_/32?if(A):2,l=_,_/=16,byte
36
#define throws
37
#define toArray(x)c
38
#define try for(;--c.c;)
39
#define void /16][(_=l+N[6+F]-66)/16]?O/=16,l/=32,O<I/16?if(C):O>I/16?this
40
#define while(k)if(2J),if(7;21H),f(0),f(4),f(4),if(H),/*
41
42
import java.io.*;
43
import java.util.*;
44
45
/**
46
 * A lame Java program.
47
 * @author  J. Random Worker
48
 */
49
class LameJavaApp
50
{
51
52
  /** The infamous Long-Winded Signature From Hell. */
53
  public static void main(String[] args)
54
      throws IOException
55
  {
56
    /* Don't get me started on this. */
57
    BufferedReader reader =
58
        new BufferedReader(new FileReader(args[0]));
59
60
    /* What, this long incantation just to print a string? */
61
    System.err.println("Hello world!");
62
63
    /* At least this is sane. */
64
    String line;
65
    while ((line = reader.readLine()) != null)
66
      System.out.println(line.length());
67
  }
68
69
  /**
70
   * Method with a needlessly long name.
71
   * @param  aSet    a set (!)
72
   */
73
  private String[] getAllStrings(Set<String> aSet)
74
  {
75
    /*
76
     * This dance is needed even in J2SE 5, which has type
77
     * templates. It was worse before that.
78
     */
79
    return aSet.toArray(new String[0]);
80
  }
81
82
}

Gruß
Roland

von PittyJ (Gast)


Lesenswert?

Ich verzichte fast komplett auf den Präprozessor. Nur für bedingte 
Kompilierungen setzte ich ein
#ifdef XXXX
ein.
Die Makros führten nur dazu, dass der Code schwerer zu verstehen und zu 
warten ist. Es gibt da nämlich eine ganze Reihe von bizarren 
Fehlermöglichkeiten, falls man Makros benutzt.

Ich nehme lieber richtige Funktionen und Klassen, in der Hoffnung, dass 
der Compiler das schon optimieren wird. Und Rechenlesitung ist doch oft 
genug vorhanden.
Und bislang bin ich mit dieser Vorgehensweise ganz gut gefahren.

von Harper B. (harper)


Lesenswert?

Micha schrieb:
> Ein Merkmal von guten Quelltexten ist deren Lesbarkeit bzw.
> Nachvollziehbarkeit.

> Welche Art, solche Sachen zu coden hatet ihr für optimal bzgl.
> Lesbarkeit und Wartbarkeit - eher sparsamen Einsatz des Präprozessors
> oder extensiven Gebrauch dieser Möglichkeit?

Für eine gute Lesbarkeit des Codes ist eine sinnvolle Struktur 
unabdingbar. Wichtig sind Interfacedefinitionen, die die Aufgaben klar 
verteilen und die verwendeten Parameter definieren. Der Umzug auf eine 
neue Hardware kommt erfahrungsgemäß genau dann, wenn man sie nicht 
erwart. Daher sollten zum Port und Pin-Definitionen auch als solche 
erscheinen.

Ob das dann mit einer Maske wie 0x0040 oder (1<<6) erfolgt ist egal 
solange es konsistent ist.

Probleme der Wartbarkeit gibt es, wenn die Aufgaben nicht klar verteilt 
werden und State Machines nicht klar implementiert werden.

Deutlich weniger wichtig ist die Verwendung der Mittel Funktion und 
Präprozessor.
Präprozesor-Zauberei schreibt sich manchmal gut hin. Es gibt aber viele 
Fälle, bei denen diese schwerer zu lesen ist, als eine (inline-) 
Funktion, die der GCC (siehe Forumname) bequem zu optimierten Code 
umwandelt.

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


Lesenswert?

Roland Praml schrieb:
> hier ein Beispiel wie man es wirklich übertreiben kann

Naja, den IOCCC zu zitieren, passt nun wirklich nicht zur ernst
gemeinten Frage (auch wenn das Pseudo-Java natürlich wirklich hübsch
ist, aber sonst wär's auch kein IOCCC-Preisträger geworden).

von J. -. (Gast)


Lesenswert?

Ich nehme an, daß es bei der Frage um größere Projekte geht, wo es 
wirklich auf die Lesbarkeit (für jedermann?) ankommt.
Dazu wurde schon das Detail von den Profis gesagt ;)

Vor ca. 2 Jahren habe ich angefangen, dem AVR im makefile mitzuteilen, 
bitte nur bedingt zu kompilieren. Das hat zwar funktioniert, war aber 
eine elende Schinderei. Es ging dabei um eine Kleinserie von fast 
identischen Funktransceivern, und das "fast" hat einen sogar als 
Privatanwender fast den letzten Nerv gekostet, weil man immer irgendwann 
vergessen hat, daß man das hinzugekommene Stück Assembler oder C nun 
doch auch noch in den anderen Modulen unterbringen kann.

Inzwischen habe ich mich vom makefile über die "global.h" zur "main.h" 
vorgearbeitet.

Das bedeutet, daß in meinen STM32-Projekten momentan tatsächlich alles 
im Ordner "common" landet, bis auf "main.c" und "main.h".
Das bedeutet, daß man man bei einer Veränderung der "Basisbibliotheken" 
in "common" immer auch die darauf zugreifenden "main.h" und "main.c" 
ändern muß. Aber mehr auch eben nicht. Das macht es einigermaßen 
überschaubar.

Weil ich verschiedene Funktransceiver und auch mehrere Displays 
verwalten will, fühle ich mich mit der derzeitigen Lösung wohl. Alle 
wichtigen Schalter liegen in "main.h". Unetrschiedliche Anforderungen in 
"main.c".
Allerdings habe ich bisher darauf verzichtet, die beiden 
Prozessorfamilien AVR und STM32 in der "main.h" auch noch zu 
berücksichtigen.
Nun pflege ich die STM32-103er auf der einen Seite, und die AVR- und 
STM32F4- auf der anderen Seite. Bei den beiden letztgenannten will ich 
zumindest deren header-Files kompatibel halten. Es ist ein Kreuz.

Und ich fürchte, daß das nur ein weiterer Anfängerfehler ist.


Die Abstraktionsebene, wo WiFi-Komponenten zusammen mit Bluetooth und 
dem (von mir heißgeliebten) RFM12 sowiee verschiedenen Prozessortypen 
allein in einer "main.h" geschaltet werden sollen, wird wahrscheinlich 
nicht funktionieren.

Aber mir gefällt die Frage, weil ich sie mir vor kurzem selbst gestellt 
habe, als ich die "main.h" mal auf Überlänge bzw. Übersichtlichkeit maß 
g

Es kommt vermutlich auch darauf an, was von Outgesourcten an Beiträgen 
kommt. Wenn da jeder seinen eigenen Stiefel von "LED_on" brät, wird das 
nichts.

von Stefan F. (Gast)


Lesenswert?

Wie kann ich eigentlich bei #include variable Dateinamen benutzen?

Mal angenommen im Makefile steht
1
-D platform=XYZ

Nun möchte ich eine Header Datei mit dem Namen deviceXYZ.h einbinden. 
Wobei XYZ im Makefile beliebig austauschbar sein soll. Wie geht das?
1
#if platform==XYZ
2
    #include "deviceXYZ.h"
3
#endif

So ist es ja doof, denn dann muss ich für jede mögliche Header Datei 
einen #if Ausdruck einfügen.

Ich hätt es gerne so:
1
#include "device"platform".h"

Ist natürlich syntaktisch falsch. Aber wie macht man das richtig?

von Rolf M. (rmagnus)


Lesenswert?

Stefan us schrieb:

> Ich hätt es gerne so:#include "device"platform".h"
>
> Ist natürlich syntaktisch falsch. Aber wie macht man das richtig?
1
-D platform_header=\"XYZ.h\"
und
1
#include platform_header

von N. G. (newgeneration) Benutzerseite


Lesenswert?

Johann L. hatte vor kurzem so etwas hier gepostet, ich finds bloß nicht 
mehr. Aber es geht auf jeden Fall (ich glaube es waren 3 oder 4 Makros 
von Nöten)
also ohne die makefile-Version von Rolf Magnus

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Alternative: der Ansatz der avr-libc.  Man macht sich ein platform.h,
und in dem steht dann sowas wie:
1
#define PLATFORM_FOO 1
2
#define PLATFORM_BAR 2
3
#define PLATFORM_MUMBLE 3
4
5
...
6
#if PLATFORM == PLATFORM_FOO
7
#  include "platform_foo.h"
8
#elif PLATFORM == PLATFORM_BAR
9
#  include "platform_bar.h"
10
#elif PLATFORM == PLATFORM_MUMBLE
11
#  include "platform_mumble.h"
12
#else
13
#  error "Unknown PLATFORM"
14
#endif

Aufruf dann mit -DPLATFORM=PLATFORM_FOO und so.

: Bearbeitet durch Moderator
von Bernd K. (prof7bit)


Lesenswert?

Stefan us schrieb:

> Wie kann ich eigentlich bei #include variable Dateinamen benutzen?

Wahrscheinlich ist auch dieser Link relevant zu dem Thema:
https://stackoverflow.com/questions/9096201/concatenate-string-in-c-include-filename

von Da D. (dieter)


Lesenswert?

PittyJ schrieb:
> Ich verzichte fast komplett auf den Präprozessor.
> Die Makros führten nur dazu, dass der Code schwerer zu verstehen und zu
> warten ist.

Axel Schwenke schrieb:
> Persönlich glaube ich, daß Aussagen der Art "der Präprozessor ist pöhse"
> hauptsächlich von Leuten kommen, die den Präprozessor nicht verstehen
> und (deshalb) Angst davor haben.

QED.

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.