rand&co. (geteilt aus: Frage zu lvalue / rvalue Referenzen)



  • Wow... echt viel. Beeindruckend.



  • So, ich hab mir die Kritik zu Herzen genommen. Ruhig an dieser Version nochmal ein bisschen meckern, dann kann ich sie weiter verbessern. 🙂

    trandom.h (Timon Random):

    #pragma once
    
    int uniform_int_rand(const int range);
    int uniform_int_rand(const int low, const int high);
    

    trandom.cpp:

    #include <memory>
    #include <random>
    using namespace std;
    
    using engine_t = mt19937; //Für x64 mt19937_64 nehmen
    
    unique_ptr<engine_t> engine_ptr(nullptr);
    
    int uniform_int_rand(const int range)
    {
    	if (!engine_ptr)
    		engine_ptr = make_unique<engine_t>();
    	uniform_int_distribution<> distribution{ 0, range };
    	return distribution(*engine_ptr);
    }
    
    int uniform_int_rand(const int low, const int high)
    {
    	if (!engine_ptr)
    		engine_ptr = make_unique<engine_t>();
    	uniform_int_distribution<> distribution{ low, high };
    	return distribution(*engine_ptr);
    }
    


  • Entferne die globale Variable! Die hattest du vorher doch auch nicht!



  • Aber dann brauche ich wieder OOP.
    Die Globale nimmt doch nur ein paar Byte auf dem Stack in Anspruch.
    Edit: Ist ja am Anfang nur ein leerer unique_ptr.



  • Timon Paßlick schrieb:

    Aber dann brauche ich wieder OOP.
    Die Globale nimmt doch nur ein paar Byte auf dem Stack in Anspruch.
    Edit: Ist ja am Anfang nur ein leerer unique_ptr.

    Darum geht es nicht. Es geht darum dass globale Variablen bestimmte Probleme machen. Weil sie dem Benutzer die Möglichkeit nehmen für unterschiedliche Dinge unterschiedliche Instanzen zu verwenden.

    Überleg dir z.B. mal was passiert wenn jemand deine uniform_int_rand Funktionen gleichzeitig aus verschiedenen Threads aufruft.



  • Aus der Engine wird doch nur gelesen, oder?
    Das ist doch ne Liste von Zahlen, bei der man mit nem Seed bestimmt, wo man anfängt und dann geht es so im Kreis durch?



  • Timon Paßlick schrieb:

    Aus der Engine wird doch nur gelesen, oder?

    Dann nehmen die Funktionen, die aus der Engine lesen, doch bestimmt als Parameter eine const engine& ?

    Schau doch mal auf http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution/operator() vorbei.

    Hm, da steht nur Generator& ohne const !

    Tja. Die Engine hat doch internen State! Ein mt19937 wird nach Initialisierung mit derselben Zahl immer dieselbe Reihenfolge weiterer Zahlen ausgeben. Das geht nur, weil jedes Lesen aus der Engine deren internen State ändert.



  • 😞



  • Und wenn ich das Thread-Problem umgehe, gehen dann Globals?



  • Timon Paßlick schrieb:

    Aus der Engine wird doch nur gelesen, oder?

    Nein! Wie könnte sie dann bei aufeinanderfolgenden Aufrufen unterschiedliche Werte liefern?

    Weiterhin:
    Der unique_ptr und new sind unnötig. Wir sind ja nicht im Java-Forum.

    Und wenn ich das Thread-Problem umgehe, gehen dann Globals?

    Geht zwar wenn du einen Mutex verwendest, besser ist es aber für diesen Fall, die Engine in der Funktion static und thread_local zu machen.



  • Timon Paßlick schrieb:

    Aber dann brauche ich wieder OOP.
    Die Globale nimmt doch nur ein paar Byte auf dem Stack in Anspruch.
    Edit: Ist ja am Anfang nur ein leerer unique_ptr.

    Habe ich auf den letzten Seiten was verpasst? Was spricht denn gegen OOP?

    Ob der Unique Pointer leer ist oder nicht, sollte keinen Unterschied für den Speicherverbrauch machen. Aber warum überhaupt ein Pointer?

    Edit: Und Globale Variablen gehen nie....



  • Der unique_ptr ist nicht einfach da, weil ich im "Java-Style" denke. Der ist da, damit nicht so viel RAM vor dem ersten Aufruf genutzt wird. Ich dachte, das spart Speicher.
    Und OOP ist nervig, finde ich. Objekt initialisieren und dann aufrufen. Stellt euch mal vor, jede Funktion würde zu einer Klasse umgeschrieben. Von der Logik her, wenn man nicht die Implementierungsdetails kennt, ist das eine Funktion. Und so lässt es sich bestimmt auch regeln. Guckt mal, wie lang Java Code ist, weil alles in eine Klasse muss.
    Edit: Und warum gibt es denn dann globale Variablen, wenn man sie nicht nutzen soll?



  • 🙄



  • Timon Paßlick schrieb:

    Der unique_ptr ist nicht einfach da, weil ich im "Java-Style" denke. Der ist da, damit nicht so viel RAM vor dem ersten Aufruf genutzt wird. Ich dachte, das spart Speicher.

    Ähm...

    Timon Paßlick schrieb:

    Stellt euch mal vor, jede Funktion würde zu einer Klasse umgeschrieben.

    Es geht hier aber nicht im irgendwelche Funktionen. Sondern um eine, die globalen State ändert. Niemand käme auf die Idee, "reine" Funktionen wie sin , cos etc. zu einer Klasse zu machen. (und in Java sind das ja auch statische Funktionen, d.h. die Klasse hat dort nur die Namespace-Funktion). Sobald du irgendwie State hast, sieht die Sache anders aus. Und der Generator hat State.

    Edit: Und warum gibt es denn dann globale Variablen, wenn man sie nicht nutzen soll?

    Warum gibt: gab es schon früher. Entfernen würde Code brechen. Ggf. will man mal damit rumspielen.
    Warum nicht: gibt sicher viel, aber es steht natürlich auch in den Core Guidelines drin:
    https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-global



  • @theta 🙂 Ich sag bloß immer was dagegen, um zu verstehen, warum ihr was tut im Gegensatz zu mir.



  • Timon Paßlick schrieb:

    Stellt euch mal vor, jede Funktion würde zu einer Klasse umgeschrieben. Von der Logik her, wenn man nicht die Implementierungsdetails kennt, ist das eine Funktion.

    Genau das ist aber auch der Knackpunkt: rand() ist "von der Logik her" eben eine Memberfunktion. Weil sie State benötigt der über einen einzigen Aufruf hinweg gültig bleibt.

    Man kann jetzt in bestimmten Fällen argumentieren dass so ein State ruhig global (oder thread-local) sein darf. Aber man kauft sich mit der Entscheidung für global/thread-local halt immer Nachteile mit ein. z.B. dass man synchronisieren muss, bzw. dass man eben thread-local braucht. Was (thread-local) einerseits nicht gratis ist, und andrerseits z.T. unerwartete Nebeneffekte hat.

    z.B. wird dir ein Shared Object unter Linux effektiv gepinnt sobald du thread-local Objekte verwendest die einen Destruktor haben. Das kann egal sein, kann aber auch stören.

    Weiters bekommst du mit globalem State in Kombination mit Threads ganz schnell Schwierigkeiten mit fork(). Meistens auch egal, weil wer forkt denn schon, aber manche Programme forken halt blöderweise doch.
    (fork-exec ist natürlich wurst, ich meine fork ohne darauffolgendes exec.)



  • Ok. 😞


Anmelden zum Antworten