Deleter shared_ptr, lambda expression, member variable



  • Hallo ihr Lieben,

    scheinbar will ich mal wieder alles auf einmal: Ich möchte einem shared_ptr sagen womit er die Ressource freigeben soll und dabei gleich noch ein Argument mitgeben, was eine Variable einer Klasse ist. 😃

    Ich hänge also am Konstruktor:

    #ifndef DCMT_WRAPPER_OMP_H_
    #define DCMT_WRAPPER_OMP_H_
    #include <iostream>
    #include <memory>
    
    extern "C"
    {
    	#include "dcmt0.6.1/include/dc.h"
    }
    
    struct DCMT_wrapper_omp
    {
    	DCMT_wrapper_omp(unsigned int);
    	int count;
    	int const seed, w, p;
    	double const max_int;
    	std::shared_ptr<mt_struct*> dcmt;
    };
    #endif
    

    Und der zugehörige Konstruktor sieht ziemlich wild aus, also gerade die Initialisierung von shared_ptr : Was genau ist daran denn falsch? Laut Compiler hängt es damit zusammen, wie das mt an den deleter weitergegeben wird?

    #include <iostream>
    #include <math.h>
    #include <omp.h>
    
    #include "DCMT_wrapper_omp.h"
    /*
    ---{}--- constructor ---{}---
    */
    DCMT_wrapper_omp::DCMT_wrapper_omp(unsigned int threads):
    count(0),seed(12),w(32),p(607),max_int(pow(2,w)-1),
    dcmt(get_mt_parameters_st(w,p,1,threads,seed,&count),
    	 [this](mt_struct** mt){ free_mt_struct_array(mt, this->count); })
    {
    	# pragma omp parallel
    	{
    		sgenrand_mt(seed, dcmt.get()[omp_get_thread_num()]);
    	}
    }
    

    Gruß,
    -- Klaus.


  • Mod

    Klaus82 schrieb:

    Was genau ist daran denn falsch?

    Klaus82 schrieb:

    Laut Compiler

    was



  • g++ -Wall -pedantic -std=c++11 -c -O2 -ffast-math -fopenmp DCMT_wrapper_omp.cpp
    In file included from DCMT_wrapper_omp.cpp:8:0:
    DCMT_wrapper_omp.h:20:55: error: wrong number of template arguments (2, should be 1)
    In file included from /usr/include/c++/4.7/bits/shared_ptr.h:52:0,
                     from /usr/include/c++/4.7/memory:87,
                     from DCMT_wrapper_omp.h:7,
                     from DCMT_wrapper_omp.cpp:8:
    /usr/include/c++/4.7/bits/shared_ptr_base.h:264:11: error: provided for ‘template<class _Tp> class std::shared_ptr’
    DCMT_wrapper_omp.cpp: In constructor ‘DCMT_wrapper_omp::DCMT_wrapper_omp(unsigned int)’:
    DCMT_wrapper_omp.cpp:15:5: error: expected identifier before ‘(’ token
    DCMT_wrapper_omp.cpp: In lambda function:
    DCMT_wrapper_omp.cpp:15:68: error: cannot convert ‘mt_struct**’ to ‘mt_struct*’ for argument ‘1’ to ‘void free_mt_struct(mt_struct*)’
    DCMT_wrapper_omp.cpp: In constructor ‘DCMT_wrapper_omp::DCMT_wrapper_omp(unsigned int)’:
    DCMT_wrapper_omp.cpp:15:72: error: expression list treated as compound expression in mem-initializer [-fpermissive]
    DCMT_wrapper_omp.cpp:15:72: error: cannot convert ‘DCMT_wrapper_omp::DCMT_wrapper_omp(unsigned int)::<lambda(mt_struct**)>’ to ‘int’ in initialization
    DCMT_wrapper_omp.cpp:19:26: error: request for member ‘get’ in ‘((DCMT_wrapper_omp*)this)->DCMT_wrapper_omp::dcmt’, which is of non-class type ‘int’
    make: *** [DCMT_wrapper_omp.o] Fehler 1
    

  • Mod

    Ist das nicht offensichtlich:

    DCMT_wrapper_omp.h:20:55: error: wrong number of template arguments (2, should be 1)

    shared_ptr hat nicht zwei Template-Argumente, sondern nur eines. Den genauen Typ des Deleters kennt nur der Konstruktor, der damit aufgerufen wird.


  • Mod

    Der nächste Fehler, der kein Folgefehler des ersten ist, ist der hier:

    DCMT_wrapper_omp.cpp:15:68: error: cannot convert ‘mt_struct**’ to ‘mt_struct*’ for argument ‘1’ to ‘void free_mt_struct(mt_struct*)’


  • Mod

    Der Typ des Deleters ist beeinflusst nicht den Typ des shared_ptr, shared_ptr benötigt nur einen Templateparameter.



  • Hallo ihr zwei,

    Arcoth schrieb:

    Der nächste Fehler, der kein Folgefehler des ersten ist, ist der hier:

    DCMT_wrapper_omp.cpp:15:68: error: cannot convert ‘mt_struct**’ to ‘mt_struct*’ for argument ‘1’ to ‘void free_mt_struct(mt_struct*)’

    Den meinte ich. 😃

    Edit:
    Oh man, war ein Tippfehler bei der Funktion, die im Deleter aufgerufen wird. 😡

    Habe es im Eingangspost editiert und das kompiliert zumindest.

    Gruß,
    -- Klaus.



  • Hallo ihr Lieben,

    ich habe dazu mal wieder eine Frage: Warum kann ich aus dem shared_ptr in Beitrag 1 nicht einfach einen unique_ptr machen und den Rest in der Source File belassen? 😕

    #ifndef DCMT_WRAPPER_OMP_H_
    #define DCMT_WRAPPER_OMP_H_
    #include <iostream>
    #include <memory>
    
    extern "C"
    {
    	#include "dcmt0.6.1/include/dc.h"
    }
    
    struct DCMT_wrapper_omp
    {
    	DCMT_wrapper_omp(unsigned int);
    	int count;
    	int const seed, w, p;
    	double const max_int;
    //	std::shared_ptr<mt_struct*> dcmt;
        std::unique_ptr<mt_struct*,void(*)(mt_struct**)> dcmt;
    };
    #endif
    

    Die Fehlermeldung

    DCMT_wrapper_omp.cpp: In constructor ‘DCMT_wrapper_omp::DCMT_wrapper_omp(unsigned int)’:
    DCMT_wrapper_omp.cpp:9:68: error: no matching function for call to ‘std::unique_ptr<mt_struct*, void (*)(mt_struct**)>::unique_ptr(mt_struct**, DCMT_wrapper_omp::DCMT_wrapper_omp(unsigned int)::<lambda(mt_struct**)>)’
    DCMT_wrapper_omp.cpp:9:68: note: candidates are:
    In file included from /usr/include/c++/4.7/memory:86:0,
                     from DCMT_wrapper_omp.h:10,
                     from DCMT_wrapper_omp.cpp:3:
    /usr/include/c++/4.7/bits/unique_ptr.h:164:2: note: template<class _Up, class> std::unique_ptr::unique_ptr(std::auto_ptr<_Up>&&)
    /usr/include/c++/4.7/bits/unique_ptr.h:164:2: note:   template argument deduction/substitution failed:
    DCMT_wrapper_omp.cpp:9:68: note:   mismatched types ‘std::auto_ptr<_Up>’ and ‘mt_struct**’
    In file included from /usr/include/c++/4.7/memory:86:0,
                     from DCMT_wrapper_omp.h:10,
                     from DCMT_wrapper_omp.cpp:3:
    /usr/include/c++/4.7/bits/unique_ptr.h:155:2: note: template<class _Up, class _Ep, class> std::unique_ptr::unique_ptr(std::unique_ptr<_Up, _Ep>&&)
    /usr/include/c++/4.7/bits/unique_ptr.h:155:2: note:   template argument deduction/substitution failed:
    DCMT_wrapper_omp.cpp:9:68: note:   mismatched types ‘std::unique_ptr<_Up, _Ep>’ and ‘mt_struct**’
    In file included from /usr/include/c++/4.7/memory:86:0,
                     from DCMT_wrapper_omp.h:10,
                     from DCMT_wrapper_omp.cpp:3:
    /usr/include/c++/4.7/bits/unique_ptr.h:142:7: note: std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = mt_struct*; _Dp = void (*)(mt_struct**); std::unique_ptr<_Tp, _Dp> = std::unique_ptr<mt_struct*, void (*)(mt_struct**)>]
    /usr/include/c++/4.7/bits/unique_ptr.h:142:7: note:   candidate expects 1 argument, 2 provided
    /usr/include/c++/4.7/bits/unique_ptr.h:136:17: note: constexpr std::unique_ptr<_Tp, _Dp>::unique_ptr(std::nullptr_t) [with _Tp = mt_struct*; _Dp = void (*)(mt_struct**); std::nullptr_t = std::nullptr_t]
    /usr/include/c++/4.7/bits/unique_ptr.h:136:17: note:   candidate expects 1 argument, 2 provided
    /usr/include/c++/4.7/bits/unique_ptr.h:130:7: note: std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer, typename std::remove_reference<_To>::type&&) [with _Tp = mt_struct*; _Dp = void (*)(mt_struct**); std::unique_ptr<_Tp, _Dp>::pointer = mt_struct**; typename std::remove_reference<_To>::type = void (*)(mt_struct**)]
    /usr/include/c++/4.7/bits/unique_ptr.h:130:7: note:   no known conversion for argument 2 from ‘DCMT_wrapper_omp::DCMT_wrapper_omp(unsigned int)::<lambda(mt_struct**)>’ to ‘void (*&&)(mt_struct**)’
    /usr/include/c++/4.7/bits/unique_ptr.h:125:7: note: std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer, typename std::conditional<std::is_reference<_Dp>::value, _Dp, const _Dp&>::type) [with _Tp = mt_struct*; _Dp = void (*)(mt_struct**); std::unique_ptr<_Tp, _Dp>::pointer = mt_struct**; typename std::conditional<std::is_reference<_Dp>::value, _Dp, const _Dp&>::type = void (* const&)(mt_struct**)]
    /usr/include/c++/4.7/bits/unique_ptr.h:125:7: note:   no known conversion for argument 2 from ‘DCMT_wrapper_omp::DCMT_wrapper_omp(unsigned int)::<lambda(mt_struct**)>’ to ‘std::conditional<false, void (*)(mt_struct**), void (* const&)(mt_struct**)>::type {aka void (* const&)(mt_struct**)}’
    /usr/include/c++/4.7/bits/unique_ptr.h:120:7: note: std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = mt_struct*; _Dp = void (*)(mt_struct**); std::unique_ptr<_Tp, _Dp>::pointer = mt_struct**]
    /usr/include/c++/4.7/bits/unique_ptr.h:120:7: note:   candidate expects 1 argument, 2 provided
    /usr/include/c++/4.7/bits/unique_ptr.h:114:17: note: constexpr std::unique_ptr<_Tp, _Dp>::unique_ptr() [with _Tp = mt_struct*; _Dp = void (*)(mt_struct**)]
    /usr/include/c++/4.7/bits/unique_ptr.h:114:17: note:   candidate expects 0 arguments, 2 provided
    make: *** [DCMT_wrapper_omp.o] Fehler 1
    


  • std::shared_ptr<mt_struct*> dcmt;
    

    Warum moechtest du einen shared_ptr von einem Pointer?



  • Das hatten wir hier diskutiert, weil es dieses C Konstrukt eben vorsieht. Ich verstehe das so, dass es einen Array von Arrays anlegt, sprich einen Pointer auf Pointer.

    Gruß,
    -- Klaus.



  • Au backe, 4 Monate ... . Aber vielleicht solltest du mit etwas einfacherem anfangen, ich empfehle meist das Buch: Die C++ Programmiersprache.

    Tip am Rande:

    max_int(pow(2,w)-1)
    
    max_int((1 << w)-1)
    

    Zu deiner Frage: Liesst du die Fehlermeldungen ueberhaupt?

    DCMT_wrapper_omp.cpp:9:68: error: ...

    Die Fehlermeldung nuetzt uns nix, wenn wir den Code dazu nicht sehen. Ich bezweifle, dass er nach 4 Monaten der gleiche ist. Angenommen es ist so, dann kann

    [this](mt_struct** mt){ free_mt_struct_array(mt, this->count); }
    

    nicht in einen Funktionszeiger konvertiert werden, also der Typ der als Deleter angegeben ist.



  • knivil schrieb:

    Zu deiner Frage: Liesst du die Fehlermeldungen ueberhaupt?

    DCMT_wrapper_omp.cpp:9:68: error: ...

    Die Fehlermeldung nuetzt uns nix, wenn wir den Code dazu nicht sehen.

    Zu deiner Antwort:
    Ich lese Fehlermeldungen scheinbar genau so wie du meine Beiträge. 😉

    Klaus82 schrieb:

    ich habe dazu mal wieder eine Frage: Warum kann ich aus dem shared_ptr in Beitrag 1 nicht einfach einen unique_ptr machen und den Rest in der Source File belassen? 😕

    Jetzt sogar hervorgehoben und direkt mit Link zum Startpost. 🙂

    Gruß,
    -- Klaus.



  • Und trotzdem war ich ne Minute schneller. Und nochmal Klartext: Ein lambda-Ausdruck mit Capture kann nicht in einen Funktionszeiger konvertiert werden.



  • knivil schrieb:

    Und trotzdem war ich ne Minute schneller. Und nochmal Klartext: Ein lambda-Ausdruck mit Capture kann nicht in einen Funktionszeiger konvertiert werden.

    Okay,
    also in der Beschreibung von shared_ptr steht für den deleter

    This shall be a callable object taking a pointer to T as argument for its functional call (where T is shared_ptr's template parameter).

    Und genau das erfüllt eben die lambda Funktion!?

    Jetzt sind wir aber beim unique_ptr wo für den deleter steht:

    Deleter object to be used to release the owned object.
    D refers to unique_ptr's second template parameter, which describes its deleter type.

    Ich bin eben etwas verwundert, weil hier auch einfach ein deleter mittels lambda Funktion gebastelt wird.

    Gruß,
    -- Klaus.



  • knivil hat ja auch nicht geschrieben dass das mit einem Lambda-Ausdruck allgemein nicht geht.
    Er hat Lambda-Ausdruck mit Capture geschreiben.

    Manchmal frag ich mich echt...



  • hustbaer schrieb:

    knivil hat ja auch nicht geschrieben dass das mit einem Lambda-Ausdruck allgemein nicht geht.
    Er hat Lambda-Ausdruck mit Capture geschreiben.

    Ah, du meinst weil

    A lambda function that doesn't capture anything can be implicitly converted to a regular function pointer

    Aber das Problem ist doch, dass ich im Deleter die Klassenvariable count benötige. Und die kann ich doch nur an die lambda Funktion übergeben, wenn ich this mittels [this] einfange, um später in der lambda Funktion mittels this->count darauf zuzugreifen.

    Wie kriege ich denn sonst - ohne capture - dieses count in die lambda Funktion?

    Gruß,
    -- Klaus.



  • Wie kriege ich denn sonst - ohne capture - dieses count in die lambda Funktion?

    Nein, die Frage ist, wie du den Typ des Deleters aenderst. Das geht wahrscheinlich nur mit einem std::function-Objekt, da der Typ aus der Lambda-Expression erst im Konstruktor bekannt ist.

    Die andere Frage, welche sich mir aufdrangt, warum benutzt du ueberhaupt einen smart pointer? Der Deleter-Body kann auch vom Destruktor der Klasse uebernommen werden. Move-Konstruktor, Move-Assignment, etc sind trivial. Weiterhin ist die Frage, warum du eine Bibliothek von 1998 benutzt, wenn Zufallszahlen mitlerweile im C++ Standard enthalten sind. Wenn jeder Thread seine eigenen Zufallszahlen bekommen soll, dann erstellt man eben die Objekte zum generieren der Zufallszahlen eben per Thread, oder man erstellt ein Seed-Array zufaellig, oder ...



  • Hallo,

    knivil schrieb:

    Die andere Frage, welche sich mir aufdrangt, warum benutzt du ueberhaupt einen smart pointer? Der Deleter-Body kann auch vom Destruktor der Klasse uebernommen werden.

    Ja, das ist mir bewusst. Ich dachte nur, dass es zum einen guter Programmierstil ist und zum anderen eben eine gute Übung. 😉

    knivil schrieb:

    Weiterhin ist die Frage, warum du eine Bibliothek von 1998 benutzt, wenn Zufallszahlen mitlerweile im C++ Standard enthalten sind. Wenn jeder Thread seine eigenen Zufallszahlen bekommen soll, dann erstellt man eben die Objekte zum generieren der Zufallszahlen eben per Thread, oder man erstellt ein Seed-Array zufaellig, oder ...

    Ja, die Frage ist natürlich berechtig und für mich nicht so einfach zu beantworten. Sagen wir, es ist ein wenig vorgegeben... 🙄

    Anhand deines Vorschlags würde ein Beispiel mittels Open MP so aussehen? Und da gibt es jetzt kein Data Racing, weil jeder Thread seinen eigenen Eintrag des vectors RNGs liest?

    Edit: Vollständiges Minimalbeispiel

    #include <iostream>
    #include <omp.h>
    #include <random>
    #include <vector>
    
    class foo
    {
    	public:
    		foo(unsigned int const seed):_engine( seed ), _rng(0.0,1.0) {}
    		auto operator()() -> double { return _rng( _engine ); }
    
    	private:
    		std::mt19937 _engine;
    		std::uniform_real_distribution<double> _rng;
    };
    
    int main()
    {
    	unsigned int const threads = 4;
    
    	omp_set_num_threads(threads);
    
    	std::vector<foo> RNGs;
    
    	for(unsigned int i = 0; i < threads; ++i)
    		RNGs.push_back( foo( 2 * i ) );
    
    	#pragma omp parallel shared (RNGs)
    	{
    		#pragma omp critical
    		{
    			std::cout << "Thread Nr. " << omp_get_thread_num() << " random number "
    								<< RNGs[ omp_get_thread_num() ]() << std::endl;
    		}
    	}
    
    	return 0;
    }
    

  • Mod

    auto operator()() -> double { return _rng( _engine ); }
    

    Also entweder du lässt das -> double weg, das wäre C++14, oder du schreibst gleich

    double operator()() { return _rng( _engine ); }
    

    .
    Sonst ist das nur überflüssig.



  • Okay,

    und bzgl. meiner Frage zum Data Racing? Gibt es eigentlich eine Möglichkeit auf Data Racing zu prüfen?

    Also nicht einen Code schreiben und rumprobieren, sondern Hilfsmittel verwenden wie gdb zum Debuggen oder valgrind, um Speicherlecks zu finden.

    Noch etwas als Geplapper: Ich frage mich z.B. zur Zeit, ob Data Racing auf eine Funktion stattfinden kann? Ich kann mir vorstellen, dass eine Variable immer nur von einem Thread beschrieben werden kann - aber scheinbar von mehreren gleiczeitig gelesen.

    Aber wie ist das mit einer Funktion? Kann die nur von einem Thread ausgeführt werden? Ein Kumpel meinte einmal salopp, dass so eine Funktion 'einfach auf den Stack gelegt wird', d.h. wenn ein thread eine Funktion benötigt, dann legt er sie auf 'seinen Stack' und verwendet sie einfach?

    Gruß,
    -- Klaus.


Log in to reply