Syntax: typedef struct



  • Hallo ihr Lieben,

    ich versuche gerade Dynamic Creator zur Erzeugung von Strömen von Zufallszahlen fürs Parallelisieren zu verstehen.

    Dabei scheint das Herzstück folgende Struktur zu sein

    typedef struct {
        uint32_t aaa;
        int mm,nn,rr,ww;
        uint32_t wmask,umask,lmask;
        int shift0, shift1, shiftB, shiftC;
        uint32_t maskB, maskC;
        int i;
        uint32_t *state;
    }mt_struct;
    

    Ich muss ja zugeben, dass ich diesen Syntax nur sehr begrenzt verstehe, aber die Implementierung mittels typedef scheint etwas mit der Speicherverwaltung zu tun haben.

    Was mich nun wundert ist z.B. die Initialisierung

    mt_struct **mtss;
    

    Wird da jetzt tatsächlich ein Objekt initialisiert? Bei einer Klasse würde ich z.B. im Konstruktor schauen was passiert, aber sowas scheint mittels der typedef Deklaration nicht gegeben zu sein? 😕
    Oder wird bei der Initialisierung eines Pointers auf einen Pointer das Objekt gleich mitinitialisiert?

    Was mich dann eben später sehr wurmt ist z.B. der Syntax

    sgenrand_mt(seed, mtss[i]);
    

    Wie kann ich denn nun auf dieses ominöse 'Objekt' mtss mittels Array-Syntax zugreifen? Müsste dieser []-Operator nicht definiert sein? 😕

    Kann mir jemand trotz Ferndiagnose einen Tipp geben?

    Gruß,
    -- Klaus.



  • Das sieht viel mehr nach C statt C++ aus.

    Erstmal vorweg: In C war es nötig, dass bei einem struct Object das Schlüsselwort 'struct' beigefügt werden musste. Also so:

    struct C
    {};
    
    void main()
    {
       struct C obj;
    }
    

    Da das nervig ist hat man typedef benutzt:

    struct C_
    {};
    typedef C_ C;
    
    // ...
    C obj;
    

    Das hat den Schreibaufwand minimiert, aber man kann die Version des Typedefs noch weiter kürzen zu:

    typedef 
     struct 
     {}
    C;
    
    // ...
    C obj;
    

    Das ist letztlich nur ein anderer Name für die Struktur. (Falls du mal Visual Studio[damals 2005 glaube ich] benutzt hast: wenn du so ein struct definierst, dann wird in der Klassenansicht eine Hashname oder sowas angezeigt, weil die IDE diese anonyme Struktur nicht kennt.)

    Zu deiner weiteren Frage:

    mt_struct ** mtss;
    

    Das ist ein zeiger auf einen Zeiger auf das Struct. Also kein Objekt selbst, und den Speicher müsstest du du auch selbst holen/freigeben.

    Und da das eh nach C aussieht (und auch kein Kon-/Destruktor definiert ist) kannst du auch nicht in einen Konstruktor schauen, was passiert. Es gibt ihn nicht.

    Nein, da es Zeiger sind, wird wohl irgendwo die Variable mtss als Array initialisiert werden, und dann kannst du auch per [] Operator drauf zugreifen.



  • aber die Implementierung mittels typedef scheint etwas mit der Speicherverwaltung zu tun haben.

    Nein, absolut nicht.

    Ich wollte gerade erklären, wofür das in C genutzt wurde, aber Skym0sh0 kam mir zuvor.



  • Sone schrieb:

    Ich wollte gerade erklären, wofür das in C genutzt wurde, aber Skym0sh0 kam mir zuvor.

    JA! Endlich hab ich mal keinen Mist erzählt und Sone musste mich nicht korrigieren ! 😃



  • Edit: Lassen wirs.

    Endlich hab ich mal keinen Mist erzählt und Sone musste mich nicht korrigieren !

    Wann habe ich dich das letzte mal korrigiert? 😕



  • Skym0sh0 schrieb:

    JA! Endlich hab ich mal keinen Mist erzählt und Sone musste mich nicht korrigieren ! 😃

    Skym0sh0 schrieb:

    void main()
    

    😃



  • Ja C...



  • Skym0sh0 schrieb:

    Ja C...

    Seit wann ist es in c void main?



  • Fein,

    jetzt würde ich natürlich gerne Nutzcode reinpacken, in Verbindung mit Parallelisieren, schließlich ist das der Sinn und Zweck des Ganzen. 😃

    Also die wichtigsten Punkte sind: initialisieren, Anzahl an Threads bekanntgeben und seeden.

    mt_struct **mtss;    // (Struktur?) initialiseren?
    int count
    const int seed = 12;
    const int w = 32;
    const int p = 607;
    const double maxint = pow(2,w)-1;
    
    // Parameter für PRNG übergeben
    mtss = get_mt_parameters_st(w, p, 1, 8, seed, &count);
    
    // seeden
    for (i=0; i<count; i++)
    {
        sgenrand_mt(seed, mtss);
    }
    

    Jetzt kann ich im Rahmen von OpenMP ein paar Threads erzeugen und jeder einzelne ist in der Lage sich mit Zufallszahlen zwischen 0 und 1 zu versorgen

    #pragma omp parallel for 
      for(int in = 0; i < count; ++i)
        genrand_mt(mtss[i]) / maxint;
    

    Also jeder Thread [i]i* erzeugt seine Zufallszahlen, indem er auf den i. Eintrag von mtss zugreift.

    Jetzt würde ich in jeden Thread gerne Nutcode reinpacken, d.h. das Erzeugen einer Zufallszahl z.B. in eine (Klassen-) Funktion verpacken, die ich dann weiterreichen kann. Zur Initialisierung einer Klasse muss ich allerdings dieses ominöse mtss weitergeben. 😕

    Ich stelle es mir z.B. so vor:

    struct rng
    {
    	rng(double const, int i, (mt_struct**) &);
    
    	double const int_max;
    	int index_thread;
    	(mt_struct**) & my_mtss;
    
    	double operator() ();
    };
    
    rng::rng(double const int_max, int i, (mt_struct**) & mt):
    int_max(int_max),index_thread(i),my_mtss(mt)
    {}
    
    double rng::operator() ()
    {
    	return genrand_mt(mtss_mt[index_thread]) / int_max;
    }
    

    Aber diese Referenz auf dieses ominöse Pointer auf Pointer Objekt knirscht gewalt in der Ausführung. 😞

    Gruß,
    -- Klaus.



  • So,

    also folgendes funktioniert! 🕶

    #include <iostream>
    #include <math.h>
    
    #include <omp.h>
    
    extern "C"
    {
    	#include "dcmt0.6.1/include/dc.h"
    }
    
    struct rng
    {
    	rng(mt_struct** &, double const &);
    	mt_struct** & my_mtss;
    	int my_index;
    	double const & my_int_max;
    
    	double operator() (int & i);
    };
    
    rng::rng(mt_struct** & my_mtss, double const & my_int_max):
    my_mtss(my_mtss),my_int_max(my_int_max)
    {}
    
    double rng::operator() (int & i)
    {
    	return genrand_mt(my_mtss[i]) / my_int_max;
    }
    
    int main()
    {
    mt_struct** mtss;
    int count;
    int const seed = 18019182;
    int const w = 32;
    int const p = 607;
    double const int_max = pow(2,w) - 1;
    
    struct rng r(mtss,int_max);
    
    int N_threads = 8;
    
    // create N_threads MTs with IDs 1 ... N_threads --> count = N_threads
    mtss = get_mt_parameters_st(w, p, 1, N_threads, seed, &count);
    
    omp_set_num_threads(N_threads);
    
    // seed N_threads MT
    #pragma omp parallel for schedule (static,1)
    	for(int i = 0; i < N_threads; ++i)
    		sgenrand_mt(seed, mtss[i]);
    
    #pragma omp parallel for schedule (static,1)
    		for(int i = 0; i < N_threads; ++i)
    		{
    			std::cout << r(i) << std::endl;
    		}
    
    	// free MTs
    	free_mt_struct_array(mtss, N_threads);
    
    	return 0;
    }
    


  • mt_struct** & my_mtss;
    

    So etwas exotisches habe ich noch nie gesehen. Gerade als Member... 😮



  • Sone schrieb:

    mt_struct** & my_mtss;
    

    So etwas exotisches habe ich noch nie gesehen. Gerade als Member... 😮

    Du bist auch kein Three Star Programmer.



  • Was soll ich dazu sagen.

    Wenn ich eine Referenz haben nöchte, dann schaue ich mir an, wie das Objekt initialisiert wird und schreibe & dahinter.

    D.h. wenn dieses ominöse Objekt eben als Pointer auf Pointer initialisiert wird ... dann kommt eben solch eine Konstruktion heraus. 😕

    Gruß,
    -- Klaus.


  • Mod

    Klaus82 schrieb:

    D.h. wenn dieses ominöse Objekt eben als Pointer auf Pointer initialisiert wird ... dann kommt eben solch eine Konstruktion heraus. 😕

    Und deswegen wirst du hier 3-star Programmer "geschimpft". Je nach Sprache gehört um solch ein Monstrum mindestens eine, vielleicht auch mehrere, Abstraktionsschicht aus class, struct, typedef, Makro oder ähnlichem drumherum. Das macht Code kürzer, wartbarer, flexibler, lesbarer, verständlicher, und so weiter. Ich wage sogar zu sagen, dass dir als Programmierer vielleicht erst mit solch einer Abstraktion selber richtig klar wird, mit welcher Art von Objekt du hantierst und wie deine Funktionen dieses Objekt beeinflussen.



  • RealProgrammer schrieb:

    Du bist auch kein Three Star Programmer.

    Ich hatte einen Vorgesetzten, der sogar 5 Sterne-Programmierer war xD



  • Dreisterneprogrammierer ist man erst ab ***. Hier sehe ich kein Problem. Der Zeiger purzelt halt so aus der API raus, solange man damit außer drüber zu iterieren nichts weiter macht, kann der so bleiben.



  • asc schrieb:

    Ich hatte einen Vorgesetzten, der sogar 5 Sterne-Programmierer war xD

    Der Absatz Zeile ist als Rope-Ähnliches Array von Blöcken gebaut, haben wie schon char**, das Kapitel besteht aus Absätzen und der Text aus Kapiteln, char****, mehrere Tabs und char*****, und weil die FileOpen-Funktion das Array der Tabs vergrößern können muss, char******. Das programmier man natürlich heimlich mit Abstraktion und macht die dann kurz vor der Abgabe wieder weg.


  • Mod

    Bashar schrieb:

    Dreisterneprogrammierer ist man erst ab ***. Hier sehe ich kein Problem. Der Zeiger purzelt halt so aus der API raus, solange man damit außer drüber zu iterieren nichts weiter macht, kann der so bleiben.

    Stimme ich nicht zu. Referenzen auf Doppelzeiger sind drei Sterne, mit anderen Mitteln. Hier soll das Doppelzeigerkonstrukt wohl eine Art Typ sein, der eben by reference an die Funktion übergeben wird. Selbst auf niedrigstem Abstraktionsniveau ist hier doch mindestens ein typedef mt_struct** my_object_type; angesagt. Das kostet nichts und bringt so viel.



  • Und davon verschwindet der Zeiger? Was soll das bringen? Der einzige Effekt ist, dass jemand, der die API auch kennt, sich in dem Code nicht mehr ohne zusätzliche Denkarbeit zurechtfindet.



  • SeppJ schrieb:

    Und deswegen wirst du hier 3-star Programmer "geschimpft". Je nach Sprache gehört um solch ein Monstrum mindestens eine, vielleicht auch mehrere, Abstraktionsschicht aus class, struct, typedef, Makro oder ähnlichem drumherum.

    Also eine Art wrapper , wenn ich den Begriff in diesem Zusammenhang richtig deute?

    Gruß,
    -- Klaus.


Anmelden zum Antworten