Forum: PC-Programmierung Init-Funktion oder if-statement mit static variable


von Stefan A. M. (Gast)


Lesenswert?

Hallo zusammen

Generell habe ich immer mal wieder Funktionen, die beim ersten Aufruf 
etwas mehr tun, als bei allen späteren Aufrufen.
z.b. muss ein Wert berechnet werden, der später immer gleich bleibt oder 
für ein Logfile wird der Header geschrieben usw..

Ich frage mich dann immer was wohl besser ist. gibt es vor- und 
nachteile der folgenden zwei Möglichkeiten:
1
foo()
2
{
3
  static int init = 1;
4
  if(init)
5
  {
6
    init = 0;
7
    //irgendwelches Zeug
8
  }
9
  //eigentlicher Funktionsteil
10
}

oder macht man generell besser zwei Funktionen:
1
int g_fooparam;
2
3
foo_init()
4
{
5
  //init zeugs
6
  g_fooparam = ...;
7
}
8
9
foo()
10
{
11
  //Funktion mit g_fooparam
12
}


Bisher habe ich immer Variante eins gewählt.

Danke für eure Meinung

Stefan

von Amateur (Gast)


Lesenswert?

Das ist gehopst wie gehüpft.

Die erste Funktion ist minimal wartbarer, da alles zusammengefasst ist.

Die zweite Version könnte sinnvoll sein, wenn die nicht Init-Funktion 
sehr häufig aufgerufen wird und Du um jeden Taktzyklus kämpfen musst.
Es kann auch sinnvoll sein die zweite Variante zu wählen, wenn Du stur 
nach Chema-F arbeitest und vor Deinem eigentlichen Programm, alles was 
-init heißt abarbeitest.

Ich bevorzuge die zweite Variante, da ich stur nach Chema Äff arbeite.

von JB (Gast)


Lesenswert?

>foo()
>{
>  static int init = 1;
>  if(init)
>  {
>    init = 0;
>    //irgendwelches Zeug
>  }
>  //eigentlicher Funktionsteil
>}

wird komplett wegoptimiert zu

//eigentlicher Funktionsteil

da init sich nicht ändern kann....

Gruß J

von (prx) A. K. (prx)


Lesenswert?

JB schrieb:
> da init sich nicht ändern kann....

Setzen, 6!

von JB (Gast)


Lesenswert?

wie?

von (prx) A. K. (prx)


Lesenswert?

Abgesehen vom Scope von "init" steht oben:
1
static int init = 1;
2
3
foo()
4
{
5
  if(init)
6
  {
7
    init = 0;
8
    //irgendwelches Zeug
9
  }
10
  //eigentlicher Funktionsteil
11
}

von JB (Gast)


Lesenswert?

"abgesehen..." jaja

von (prx) A. K. (prx)


Lesenswert?

JB schrieb:
> "abgesehen..." jaja

Der Scope ist bezogen auf die gezeigten Zeilen irrelevant. Die Zuweisung 
(Initialisierung) in "static int init = 1;" erfolgt nur einmal, in "int 
init = 1;" jedesmal.

: Bearbeitet durch User
von JB (Gast)


Lesenswert?

Heisst ich kann in einer Funktion (foo) eine Variable (init) lokal 
definieren auf die ich im weiteren global zugriff habe?

von Stefan A. M. (Gast)


Lesenswert?

A. K. schrieb:
> Der Scope ist bezogen auf die gezeigten Zeilen irrelevant. Die Zuweisung
> in "static int init = 1;" erfolgt nur einmal, in "int init = 1;"
> jedesmal.

Verstehe ich nicht.. Also ich weiss, dass es funktioniert. Mein Header 
z.b. wird nur einmal geschrieben. alses was im
1
if(init)
2
...
steht passiert bei mir genau ein mal.

von JB (Gast)


Lesenswert?

void foo()
{
   static int a;
}

void baa()
{
   static int b;

   a = 10; //Zuweisung an a aus foo
}

int a; //Was passiert hier?

int main
{
   int a = 10; //Und hier?

}

Danke für deine Mühe und Erklärung, man lernt halt nie aus bei C ;)

von (prx) A. K. (prx)


Lesenswert?

Bleib mal beim obigen Code und der obigen Aufgabe. Nur darauf bezog sich 
meine Aussage. Aber wenn du es wirklich so drauf anlegst:

JB schrieb:
>>foo()
>>{
>>  static int init = 1;
>>  if(init)
>>  {
>>    init = 0;
>>    //irgendwelches Zeug
>>  }
>>  //eigentlicher Funktionsteil
>>}
>
> wird komplett wegoptimiert zu
>
> //eigentlicher Funktionsteil

Die Aussage ist mit und ohne "static" falsch. Nur auf völlig 
verschiedene Weise.

von Stefan A. M. (Gast)


Lesenswert?

hmm, also das schockt mich jetzt etwas bzw. ich kanns fast nicht 
glauben.

soll das heissen, meine
1
static int init;
sind alle global abrufbar?
Ich hätte jetzt immer gedacht der scope dieser inits ist genau meine 
funktion foo() und bar() usw.


schliesslich habe ich sicher 5 funktionen die genauso beginnen. und 
probleme gabs meines wissens bis jetzt nicht.

von JB (Gast)


Lesenswert?

>Ich hätte jetzt immer gedacht der scope dieser inits ist genau meine
>funktion foo() und bar() usw.

Ich auch...

jetzt wird es spannend

von (prx) A. K. (prx)


Lesenswert?

Stefan A. M. schrieb:
> static int init;

Nein.

> Ich hätte jetzt immer gedacht der scope dieser inits ist genau meine
> funktion foo() und bar() usw.

Ist auch so.

von Stefan E. (sternst)


Lesenswert?

JB schrieb:
> Heisst ich kann in einer Funktion (foo) eine Variable (init) lokal
> definieren auf die ich im weiteren global zugriff habe?

Nein. Sie wird zwar global angelegt (nur einmal initialisiert und behält 
ihren Wert von Funktionsaufruf zu Funktionsaufruf), aber du hast keinen 
globalen Zugriff drauf, sondern nur lokal in der Funktion.

von JB (Gast)


Lesenswert?

>> Ich hätte jetzt immer gedacht der scope dieser inits ist genau meine
>> funktion foo() und bar() usw.

>Ist auch so.

Dann erklär doch mal wie du die dann änderst außerhalb von foo?

von (prx) A. K. (prx)


Lesenswert?

A. K. schrieb:
> Abgesehen vom Scope von "init" steht oben:
>
1
> static int init = 1;
2
> 
3
> foo()
4
> {
5
>   if(init)
6
>   {
7
>     init = 0;
8
>     //irgendwelches Zeug
9
>   }
10
>   //eigentlicher Funktionsteil
11
> }
12
>

Da diese Aussage offenbar von niemandem verstanden wird, mal 
umformuliert:

Dieser Code entspricht dem analogen Ausschnitt des Code des ersten 
Posting, mit Ausnahme der Sichtbarkeit von "init".

von JB (Gast)


Lesenswert?

Ok jetzt hab ich's

weil

>...Sie wird zwar global angelegt (nur einmal initialisiert und behält
>ihren Wert von Funktionsaufruf zu Funktionsaufruf), aber du hast keinen
>globalen Zugriff drauf, sondern nur lokal in der Funktion.

wird nichts wegoptimiert. Danke!

Gruß Jonas Biensack

von Wilhelm M. (wimalopaan)


Lesenswert?

Stefan A. M. schrieb:
> Hallo zusammen
>
> Generell habe ich immer mal wieder Funktionen, die beim ersten Aufruf
> etwas mehr tun, als bei allen späteren Aufrufen.
> z.b. muss ein Wert berechnet werden, der später immer gleich bleibt oder
> für ein Logfile wird der Header geschrieben usw..
>
> Ich frage mich dann immer was wohl besser ist. gibt es vor- und
> nachteile der folgenden zwei Möglichkeiten:
>
>
1
> foo()
2
> {
3
>   static int init = 1;
4
>   if(init)
5
>   {
6
>     init = 0;
7
>     //irgendwelches Zeug
8
>   }
9
>   //eigentlicher Funktionsteil
10
> }
11
>
>

Nur als Hinweis: der Code enthält eine race-condition. Ist also falsch, 
wenn von mehreren Threads / ATs verwendet.

Also: entweder pthread_once() oder ein atomic_flag mit 
atomic_test_and_test().

Am besten von Anfang an richtig machen, denn solche Fehler findet man 
nur sehr schwer später ...

von Peter II (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Nur als Hinweis: der Code enthält eine race-condition. Ist also falsch,
> wenn von mehreren Threads / ATs verwendet.
>
> Also: entweder pthread_once() oder ein atomic_flag mit
> atomic_test_and_test().
>
> Am besten von Anfang an richtig machen, denn solche Fehler findet man
> nur sehr schwer später ...

99% vom code sind nicht Threadsafe - das ist auch normal so. Es macht 
überhaupt keine sinn jede Funktion versuchen sicher zu machen.

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Nur als Hinweis: der Code enthält eine race-condition. Ist also falsch,
> wenn von mehreren Threads / ATs verwendet.

Weshalb man spätestens in solchen Fällen besser auf die zweite anfangs 
präsentierte Variante ausweicht: eine getrennte Init-Funktion.

Thread-Locking für solchen Kleinkram wär schon ziemlich mit Kanonen auf 
Spatzen geschossen.

von Wilhelm M. (wimalopaan)


Lesenswert?

A. K. schrieb:
> Wilhelm M. schrieb:
>> Nur als Hinweis: der Code enthält eine race-condition. Ist also falsch,
>> wenn von mehreren Threads / ATs verwendet.
>
> Weshalb man spätestens in solchen Fällen besser auf die zweite anfangs
> präsentierte Variante ausweicht: eine getrennte Init-Funktion.

Noch schlimmer ....

>
> Thread-Locking für solchen Kleinkram wär schon ziemlich mit Kanonen auf
> Spatzen geschossen.

Kleinkram? Wir wissen doch gar nicht um was es geht? Initialisierung 
eines Pseudozufallszahlengenerators? Simulation mit 10.000 Threads?

: Bearbeitet durch User
von Mikro 7. (mikro77)


Lesenswert?

Wow, nach dem ersten "wegoptimiert" Hijack kommt jetzt der Mutithreading 
Hijack. ;-)

Stefan A. M. schrieb:
> Ich frage mich dann immer was wohl besser ist. gibt es vor- und
> nachteile der folgenden zwei Möglichkeiten:

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
>> Weshalb man spätestens in solchen Fällen besser auf die zweite anfangs
>> präsentierte Variante ausweicht: eine getrennte Init-Funktion.
>
> Noch schlimmer ....

Nö. Denn die Initialisierungsphase wird oft zu einem Zeitpunkt 
stattfinden, in dem um gleiche Ressourcen konkurrierende Worker-Threads 
noch nicht existieren.

> Simulation mit 10.000 Threads?

Eben. Wenn du etwas im Worker-Thread im laufzeitrelevanten Teil mit 
Locks versiehst, das du locker heraustrennen könntest, dann kann es 
passieren, dass dir das nutzlose Locking die halbe Leistung frisst.

von Wilhelm M. (wimalopaan)


Lesenswert?

A. K. schrieb:
> Wilhelm M. schrieb:
>>> Weshalb man spätestens in solchen Fällen besser auf die zweite anfangs
>>> präsentierte Variante ausweicht: eine getrennte Init-Funktion.
>>
>> Noch schlimmer ....
>
> Nö. Denn die Initialisierungsphase wird oft zu einem Zeitpunkt
> stattfinden, in dem um gleiche Ressourcen konkurrierende Worker-Threads
> noch nicht existieren.

Eine Schnittstelle sollte zu allererst leicht richtig und schwer falsch 
zu benutzen sein. Und den internen Zustand nach aussen zu transportieren 
ist nie eine gute Idee ...

>
>> Simulation mit 10.000 Threads?
>
> Eben. Wenn du etwas im Worker-Thread im laufzeitrelevanten Teil mit
> Locks versiehst, das du locker heraustrennen könntest, dann kann es
> passieren, dass dir das nutzlose Locking die halbe Leistung frisst.

atomic_flag ist lock-free.

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Am besten von Anfang an richtig machen, denn solche Fehler findet man
> nur sehr schwer später ...

Das Code, der nicht für parallele Threads mit konkurrierenden Ressourcen 
geschrieben wurde, einen entsprechenden Update nicht übersteht, ohne ein 
paar Federn zu lassen, ist wohl selbstverständlich.

Dass man deshalb nicht jedes Programm gleich thread-fest programmieren 
muss, allerdings auch. Nicht bei jeder Anwendung lohnt sich der Zirkus.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> atomic_flag ist lock-free.

Schonmal über die sukzessive Entwicklung der x86er auf Hardware-Ebene 
drauf geachtet, was ein LOCK XCHG kosten kann?

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Stilfrage: Um sicherzustellen, dass die Initialisierung nicht als 
Seiteneffekt eines Übertrampelns der Variablen init doch mehrfach 
aufgerufen wird (ja, sowas passiert, und laut Murphy nur im Feld, und es 
dauert ewig, das zu finden), ziehe ich eine strenge Prüfung vor:

#define INITONCESIG 0x54543127

foo()
{
  static int init = INITONCESIG;
  if(init == INITONCESIG)
  {
    init = ~INITONCESIG;
    //irgendwelches Zeug
  }
  //eigentlicher Funktionsteil
}

oder irgendwie sonst so, dass es schon mit Doppelmurphy zugehen muss, um 
die Initialisierung doch mehrfach durchlaufen zu lassen...

mit der Strategie kann man übrigens auch das Verständnisproblem mit der 
Einmalinitialisierung umgehen:

foo()
{
  static int init;
  if(init != INITONCESIG)
  {
    init = INITONCESIG;
    //irgendwelches Zeug
  }
  //eigentlicher Funktionsteil
}

(was aber impliziert, dass init in die .bss gelegt wird; liegt sie in 
einem vom Startupcode nicht angefassten Bereich, ist die 
Wahrscheinlichkeit relativ hoch, dass sie nach einem Restart ohne power 
up genau den Wert hat, bei dem sie aufgehört hat...)

von Wilhelm M. (wimalopaan)


Lesenswert?

A. K. schrieb:
> Wilhelm M. schrieb:
>> Am besten von Anfang an richtig machen, denn solche Fehler findet man
>> nur sehr schwer später ...
>
> Das Code, der nicht für parallele Threads mit konkurrierenden Ressourcen
> geschrieben wurde, einen entsprechenden Update nicht übersteht, ohne ein
> paar Federn zu lassen, ist wohl selbstverständlich.
>
> Dass man deshalb nicht jedes Programm gleich thread-fest programmieren
> muss, allerdings auch. Nicht bei jeder Anwendung lohnt sich der Zirkus.

Mir ist doch auch klar, dass es auf dieser Welt viel Code gibt, der 
nicht MT-safe ist.

Aber: wir sind doch seit langem in einer Welt, in der Multi-Cores die 
Regel und nicht die Ausnahme sind. Deswegen bin ich der Meinung, dass 
Code, der nicht MT-Safe ist, eine Optimierung (!) einer Problemlösung 
darstellt, und nicht die allg. Lösung ist. Und wie D.Knuth schon sagte: 
premature optimization is the root of all evil.

Bottom line: entweder MT-safe oder extrem deutlich machen bzw. 
unkompilierbar machen, wenn dem nicht so ist.

Und bitte: es muss nicht jeder einverstanden sein!

Mein erster Post in diesem Thread begann mit: Nur als Hinweis ...

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Ruediger A. schrieb:

>
> foo()
> {
>   static int init;
>   if(init != INITONCESIG)
>   {
>     init = INITONCESIG;
>     //irgendwelches Zeug
>   }
>   //eigentlicher Funktionsteil
> }
>

oops, ist natürlich Käse, weil diese Lösung auch wieder das 
Übertrampelproblem der Originallösung hat... sorry 4 the noise.

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Und wie D.Knuth schon sagte:
> premature optimization is the root of all evil.

Ich habe zwar dafür grad keinen bessere Spruch drauf, aber Code 
thread-safe aussehen zu lassen, ohne das in allen relevanten Situationen 
getestet zu haben, ist sowas wie die Wurzel der Wurzel allen Übels. ;-)

> Und bitte: es muss nicht jeder einverstanden sein!

Hoffentlich! Wär echt langweilig.

von A. S. (Gast)


Lesenswert?

Zur eigentlichen Frage:

die erste Version ist z.B. besser, wenn
- eine getrennte Initfunktion sonst nicht üblich ist
- das Init erst nachträglich notwendig wird (und 50 ähnliche Funktionen 
ohne auskommen)
- es wirklich nur eine Funtkion ist (und kein aufwendiges Modul mit 
dutzenden Funktionen)

die zweite Version ist
- thematisch "sauberer",
- erlaubt evt. die Neu-Initialisierung
- erlaubt ggf. unterschiedliche Parameter fürs Init
- spart auf den ersten Blick zur Laufzeit eine If-Abfrage (auf den 
zweiten Blick wird es dadurch viel fragiler)


Darum ruhig beides verbinden:
a) explizite Init-Funktion(en)
b) Trotzdem die variante 1, bei der dann ggf. die Init-Funktion (von a) 
aufgerufen wird. Ggf mit Trace-Infos, Debug-Ausgaben, Assert, ... . Dazu 
muss das init natürlich auf File-Sichtbarkeit gehoben werden. Auch ist 
ein "sinnvoller" default zu wählen, wenn Init parametriert möglich ist.

c) ich verwende die inverse logic:
1
static int init=0;
2
3
   if(!init)
4
   {
5
      init = 1;
6
   }
7
]
In Deinem Fall liest sich init als imperativ (initialisiere!), in meinem 
als Zustandsfrage (NOT initialisiert). Daher ist init zweideutig. Du 
könntest Dein Flag z.B. doInit nennen, ich meines initialized. Ich 
bevorzuge meine Variante, da 0 default ist.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Achim S. schrieb:
> c) ich verwende die inverse logic:
>
1
> static int init=0;
2
> 
3
>    if(!init)
4
>    {
5
>       init = 1;
6
>    }
7
> ]
8
>

Hmmmm, nun können wir anfangen Haare zu spalten...

Deine und meine (1.) Variante machen eine strengere Prüfung als der OP, 
weil bei uns jeweils die Prüfbedingung bei genau einem von 2^32 (oder 
wie gross immer ein int ist) zutrifft, beim OP bei (2^32)-1, d.h. im 
Falle eines undefinierten Wertes (z.B. durch Übertrampeln) ist die 
Wahrscheinlichkeit, die Initialisierung fälschlich mehrfach zu 
durchlaufen, beim Originalcode wesentlich höher.

Aber: 0 ist als Matchwert für den Einmaldurchlauf eher Suboptimal, da im 
Laufe eines Programmablaufs die 0 wesentlich häufiger geschrieben wird 
als jeder Andere Wert. Ich ziehe eine Signatur vor, die extrem 
unwahrscheinlich irgendwo anders vorkommt (also NICHT 0, NICHT 
0xffffffff sowie nichts, was in den adressierbaren Bereich des boards 
fällt).

Das ist Haarspalterei, aber die Haare sind sehr dick. ich weiss nicht 
wie oft bei meinen Kunden genau diese Fälle aufgetreten sind, und diese 
Dinge herunterzutracen sind enorm zeitraubend...

von Wilhelm M. (wimalopaan)


Lesenswert?

Ruediger A. schrieb:
> Achim S. schrieb:
>> c) ich verwende die inverse logic:
>>
1
>> static int init=0;
2
>>
3
>>    if(!init)
4
>>    {
5
>>       init = 1;
6
>>    }
7
>> ]
8
>>
>
> Hmmmm, nun können wir anfangen Haare zu spalten...

> Aber: 0 ist als Matchwert für den Einmaldurchlauf eher Suboptimal, da im
> Laufe eines Programmablaufs die 0 wesentlich häufiger geschrieben wird
> als jeder Andere Wert. Ich ziehe eine Signatur vor, die extrem
> unwahrscheinlich irgendwo anders vorkommt (also NICHT 0, NICHT
> 0xffffffff sowie nichts, was in den adressierbaren Bereich des boards
> fällt).

Dann nehmt doch bitte bool! Passt semantisch, int ist semantisch falsch 
...

von Stefan A. M. (Gast)


Lesenswert?

alles klar wie kloßbrühe

Thread safeness ist kein Thema bei mir. Jede Funktion wird nur von je 
genau einem Thread benutzt.

Ich bleibe also bei meiner Lösung Nr. 1.
weil ich
a) möglichst wenig globale (sichtbare) Variablen will.
b) nicht vergessen will, das foo_init() aufzurufen.


Wenn ich den youtube-lessions zu Branch-Prediction glauben darf, wird 
mein Prozessor sowieso sehr schnell merken, dass der init-teil von foo() 
niemals wieder aufgerufen wird. Daher auch keine 
Geschwindigkeitseinbusse.

Ausser der compiler verzichtet wegen eines grossen init-teils auf 
inlining, was er sonst vielleicht machen würde.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

>
> Dann nehmt doch bitte bool! Passt semantisch, int ist semantisch falsch
> ...

Hat zwar überhaupt nix mit meinem Punkt zu tun, aber auch egal. OP ist 
zufrieden, was will man mehr.

von Nase (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Aber: wir sind doch seit langem in einer Welt, in der Multi-Cores die
> Regel und nicht die Ausnahme sind. Deswegen bin ich der Meinung, dass
> Code, der nicht MT-Safe ist, eine Optimierung (!) einer Problemlösung
> darstellt, und nicht die allg. Lösung ist.

Dann musst du das aber auch im gesamten Interface durchziehen. Gibt ja 
sicher noch andere Funktionen, die auf einen initialisierten Zustand 
angewiesen sind...
Im Prinzip wird damit jede Funktion um ein "if(init)" bereichert.

Damit ist man dann recht schnell beim Themenkomplex /Lazy 
Initialization/. Der wiederum ist eine Optimierung (!) der Problemlösung 
und nicht die allgemeine Lösung. Die Optimierung besteht darin, 
Ressourcen erst bei Gebrauch anzufordern (=zu initialisieren) - 
möglicherweise spart man damit eine Initialisierung. Darum sollte man 
das vermeiden und lieber eine explizite Initialisierungs-Funktion 
bereitstellen.

Lazy-Init geht in C++ überaus elegant, indem man z.B. das pimpl-Idiom 
implementiert und dabei die Zugriffs-Operatoren (* und ->) überläd.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nase schrieb:
> Wilhelm M. schrieb:
>> Aber: wir sind doch seit langem in einer Welt, in der Multi-Cores die
>> Regel und nicht die Ausnahme sind. Deswegen bin ich der Meinung, dass
>> Code, der nicht MT-Safe ist, eine Optimierung (!) einer Problemlösung
>> darstellt, und nicht die allg. Lösung ist.
>
> Dann musst du das aber auch im gesamten Interface durchziehen. Gibt ja
> sicher noch andere Funktionen, die auf einen initialisierten Zustand
> angewiesen sind...
> Im Prinzip wird damit jede Funktion um ein "if(init)" bereichert.

Hoffentlich nicht, eine "Funktion" mit einem Zustand ist keine (reine) 
Funktion mehr. Und bei reinen Funktionen hat man keinen Stress mit 
Nebenläufigkeit.

> Damit ist man dann recht schnell beim Themenkomplex /Lazy
> Initialization/. Der wiederum ist eine Optimierung (!) der Problemlösung
> und nicht die allgemeine Lösung. Die Optimierung besteht darin,
> Ressourcen erst bei Gebrauch anzufordern (=zu initialisieren) -
> möglicherweise spart man damit eine Initialisierung. Darum sollte man
> das vermeiden und lieber eine explizite Initialisierungs-Funktion
> bereitstellen.

Sowas nennt man Konstruktor ...

> Lazy-Init geht in C++ überaus elegant, indem man z.B. das pimpl-Idiom
> implementiert

Ach was ;-)
PImpl allein reicht aber nicht: CoW auch noch ...

> und dabei die Zugriffs-Operatoren (* und ->) überläd.

Und damit hat das gar nix zu tun ...

Aber PImpl hat noch viele andere Vorteile (die schon fast entscheidender 
sind).

: Bearbeitet durch User
von NurNochEinZeile (Gast)


Lesenswert?

Ich würde in eine Funktion nur so wenig Wissen über den Kontext 
implementieren, wie nötig.

Bsp.: Header hinzufügen vor dem 1. Eintrag.
Es gibt eine Funktion, die sich um das Einfügen eines Headers kümmert 
und andere für das Einfügen weitere Einträge -> 
Single-Responsibility-Prinzip.

AddHeader()
{
   //Header hinzufügen
}

AddEntry(Logeintrag entry)
{
   //Eintrag hinzufügen
}

Trivial, aber wichtig: ein Header ist kein Entry!
Warum sollte eine Funktion entscheiden, was, wann zu tun ist?

Irgendwann wäre vielleicht die max. Anzahl an Einträgen erreicht. Die 
Einträgen werden persistiert und die Liste geleert. Und dann?
Im 1. Beispiel: Programmneustart, damit init wieder auf 0 gesetzt wird. 
Ups ^^
Dein 2. Bsp. ist schon besser.

Wer entscheidet nun, welche Funtkion zu nehmen ist?
Meine Regel: nur derjenige, der das Wissen darüber in der Hand haben 
muss.
Im Logeintrag Bsp. wäre das beim Anlegen eines Objekts der Fall, das die 
Einträge speichert:

CreateLog()
{
  //Speicher allozieren.
  AddHeader();
}

Wer auch immer das Loggen starten möchte, muss CreateLog() aufrufen.
Hoffe, das bringt dich weiter.

von MaWin (Gast)


Lesenswert?

Über die trivialsten "Probleme" werden the längsten Threads geschrieben.

von Nase (Gast)


Lesenswert?

Wilhelm M. schrieb:
>> Im Prinzip wird damit jede Funktion um ein "if(init)" bereichert.
>
> Hoffentlich nicht, eine "Funktion" mit einem Zustand ist keine (reine)
> Funktion mehr. Und bei reinen Funktionen hat man keinen Stress mit
> Nebenläufigkeit.
Aha, und welchen Sinn hat dann die ganze Geschichte, die wir hier gerade 
durchdiskutieren? Schon die statische "init"-Variable in den 
anfänglichen Beispielen stellt einen Zustand dar.

Wilhelm M. schrieb:
> Sowas nennt man Konstruktor ...
Nein, das ist ja explizit gerade nicht der Konstruktor. Es geht 
explizit nicht um RAII.
Sowas kann man in anderem Kontext z.B. Singleton nennen, oder 
Construction-on-first-usage.

Wilhelm M. schrieb:
> PImpl allein reicht aber nicht: CoW auch noch ...
Nein, es geht auch explizit nicht um CoW.
Es geht darum, den Konstruktur erst beim Gebrauch zu rufen oder sogar 
dann erst dynamisch Speicher anzufordern. CoW will man in diesem Fall ja 
gerade nicht.

Wilhelm M. schrieb:
> Und damit hat das gar nix zu tun ...
Doch, genau damit hat das zu tun. Denn genau dort kann man die MT-feste 
Konstruktion vornehmen. Das hat den schönen Vorteil, dass man noch einen 
Container drumherum hat.

Gut nachvollziehbares Beispiel: QGlobalStatic aus Qt.

von Stefan a. m. (Gast)


Lesenswert?

MaWin schrieb:
> Über die trivialsten "Probleme" werden the längsten Threads geschrieben.


joa krass. Erste Frage auf der Homepage, und in diesem Subforum an der 
Spitze dabei.
Für mich ist die Antwort klar. Solange keine Thread safeness oder 
re-init nötig wird, bleibt Variante 1 Favorit.

zum Thema bool: ich mag keine bools. in Windows sind das sowieso nur 
int, dann kann ich gleich das nehmen. Ausser ich kriege vom Kollegen 
matlab-code, dann sind es unsigned char.

Variante 2 wird vielleicht dann interessant, wenn sie inline-bar wird 
und extrem oft aufgerufen wird.

von Wilhelm M. (wimalopaan)


Lesenswert?

Stefan a. m. schrieb:
> MaWin schrieb:
>> Über die trivialsten "Probleme" werden the längsten Threads geschrieben.

> zum Thema bool: ich mag keine bools.

Das ist natürlich ein starkes Argument ...

> in Windows sind das sowieso nur
> int, dann kann ich gleich das nehmen.

... und der Rest ist ein String ...

Im Ernst: an einer streng-typisierten Sprache sind Datentypen so 
ziemlich das wichtigste. Für C-Verfechter ist das etwas schwer sichtbar 
(auch wenn das Beispiel natürlich auch C++ sein könnte), in C++ und 
anderen Sprachen (meist OO) sind domänen-spezifische DT der Schlüssel 
zum Erfolg ...

von nicht“Gast“ (Gast)


Lesenswert?

Eine Funktion, die bei gleichen Aufrufen unterschiedliche Sachen macht?

Dass ist ganz klar ein no go. Damit landet man ganz schnell in der 
Debugging Hölle. Die Init Funktion ist dagegen sauber und ordentlich. 
Der Inhalt des If sollte eh in eine Funktion ausgelagert werden, damit 
man das testen kann. Dieser kann man auch den Namen Init geben und 
direkt aufrufen.
Dann weiß auch Anwender, der deine Funktionen benutzt, was da passiert.

von A. S. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Im Ernst: an einer streng-typisierten Sprache sind Datentypen so
> ziemlich das wichtigste. Für C-Verfechter ist das etwas schwer sichtbar
> (auch wenn das Beispiel natürlich auch C++ sein könnte), in C++ und
> anderen Sprachen (meist OO) sind domänen-spezifische DT der Schlüssel
> zum Erfolg ...

Es ist richtig, bool zu nehmen. C89 oder C99 kennt bool aber nicht. 
Darum macht es hier bei foo auch keinen Sinn und dann ist int als 
nativer Typ des Compilers richtig.

Man kann lange diskutieren, ob Dinge dadurch einfacher werden, dass man 
auch bei Spatzen seine Kanonen zum Einsatz bringt. Wenn jemand bei einem 
mittleren Projekt kein Bool hat, läuft irgend was falsch. Schlimmer 
ist aber, wenn er 2 Bool-Typen hat, eines von dem Beispiel hier und 
eines von irgendeiner lib.

von Wilhelm M. (wimalopaan)


Lesenswert?

Achim S. schrieb:
> Wilhelm M. schrieb:
>> Im Ernst: an einer streng-typisierten Sprache sind Datentypen so
>> ziemlich das wichtigste. Für C-Verfechter ist das etwas schwer sichtbar
>> (auch wenn das Beispiel natürlich auch C++ sein könnte), in C++ und
>> anderen Sprachen (meist OO) sind domänen-spezifische DT der Schlüssel
>> zum Erfolg ...
>
> Es ist richtig, bool zu nehmen. C89 oder C99 kennt bool aber nicht.
> Darum macht es hier bei foo auch keinen Sinn und dann ist int als
> nativer Typ des Compilers richtig.

Nicht (mehr) richtig (C99 / C11):

http://en.cppreference.com/w/c/language/arithmetic_types#Boolean_type

: Bearbeitet durch User
von Kaj (Gast)


Lesenswert?

Achim S. schrieb:
> C89 oder C99 kennt bool aber nicht.
Schon mal was von stdbool.h gehoert?

von Wilhelm M. (wimalopaan)


Lesenswert?

Kaj schrieb:
> Achim S. schrieb:
>> C89 oder C99 kennt bool aber nicht.
> Schon mal was von stdbool.h gehoert?

Man, steht doch genau ein Betrag vor Dir ...

von MaWin (Gast)


Lesenswert?

Stefan A. M. schrieb:
> foo()

Ich würde die Funktion eher bar() nennen. Oder vielleicht noch baz().
Ist viel harmonischer.

von JB (Gast)


Lesenswert?

foobar junge!

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.