boost::bind mit rvalue-Referenz



  • Hallo zusammen,

    kann man boost-bind mit rvalue-Referenzen nutzen?
    Also ich möchte in folgendem Beispiel aus der Referenz eine rvalue-Referenzen machen.

    #include <memory>
    #include <iostream>
    #include <boost/bind.hpp>
    #include <boost/function.hpp>
    
    class Foo{};
    
    void foobar(Foo& foo){
            std::cout << "foobar" << std::endl;
    };
    
    int main(){
            Foo foo;
            boost::function<void ()> f(boost::bind(&foobar, boost::ref(foo)));
            f();
    }
    

    Ist es möglich?

    Gruß,
    XSpille



  • Schon ausprobiert?



  • XSpille schrieb:

    kann man boost-bind mit rvalue-Referenzen nutzen?
    Also ich möchte in folgendem Beispiel aus der Referenz eine rvalue-Referenzen machen.

    #include <memory>
    #include <iostream>
    #include <boost/bind.hpp>
    #include <boost/function.hpp>
    
    class Foo{};
    
    void foobar(Foo& foo){
            std::cout << "foobar" << std::endl;
    };
    
    int main(){
            Foo foo;
            boost::function<void ()> f(boost::bind(&foobar, boost::ref(foo)));
            f();
    }
    

    Ist es möglich?

    Ist was möglich? Geht das ein bisschen genauer?

    Ich rate mal: Die Funktion foobar soll eine Rvalue-Referenz entgegennehmen.

    Gegenfrage: Was soll das?

    Zu bind: Die gebundenen Objekte werden immer als Lvalue-Ausdruck weitergeleitet. Das ist auch gut so, da man sonst ungewollte Mutationen bzgl der gebundenen Objekte stattfinden könnten.

    Lambdas können helfen:

    class Foo{};
    
    void foobar(Foo&& foo){
            std::cout << "foobar" << std::endl;
    };
    
    int main(){
            Foo foo;
            boost::function<void ()> f = [&foo]{foobar(std::move(foo));};
            f();
    }
    

    Da die Funktion foobar eine Rvalue-Referenz entgegen nimmt und diese Art Referenz sich die immer nur auf Objekte bezieht, die verändert werden dürfen, ohne dass es jemanden stören würde, darf foobar hier das Foo-Objekt verändern. Das musst Du bedenken, wenn Du f mehrmals aufrufen willst.



  • Ethon schrieb:

    Schon ausprobiert?

    Jupp...

    void foobar(Foo&& foo){
            std::cout << "foobar" << std::endl;
    };
    

    Ich hab es so probiert:

    boost::function<void ()> f(boost::bind(&foobar, foo));
    

    und

    boost::function<void ()> f(boost::bind(&foobar, boost::bind(&std::move, foo)));
    

    krümelkacker schrieb:

    Gegenfrage: Was soll das?

    Ich hab eine Member-Funktion mit einer rvalue-Referenz.
    Diese Member-Funktion soll der Einstiegspunkt für einen neuen Thread sein.
    Sie wird folglich nur einmal aufgerufen. (Das hatte ich schon bedacht und erst vermutet, dass es deswegen evtl. nicht geht)

    Jetzt möchte ich an boost::thread ein boost::bind übergeben. Also eigentlich
    später für eine Member-Funktion. Ich wollte das Beispiel nur einfach halten.

    Gruß,
    XSpille



  • Ich hab eine Member-Funktion mit einer rvalue-Referenz.

    Warum? Das klingt alles sehr komisch!

    Werd doch mal konkreter. Zeig doch mal ein repräsentatives Beispiel. Was für eine Memberfunktion?



  • krümelkacker schrieb:

    Ich hab eine Member-Funktion mit einer rvalue-Referenz.

    Warum? Das klingt alles sehr komisch!

    Werd doch mal konkreter. Zeig doch mal ein repräsentatives Beispiel. Was für eine Memberfunktion?

    Naja... Dann pack ich mal einiges an Source-Code aus...
    Ich hoffe danach verstehst du, warum ich das mache bzw. ich, warum ich was anders machen sollte...

    #include <memory>
    #include <iostream>
    #include <vector>
    #include <boost/bind.hpp>
    #include <boost/function.hpp>
    #include <boost/asio.hpp>
    
    class Request /* vereinfachte Klasse */{
    public:
    	void add_info(std::vector<char>& vector, unsigned int i){}
    	bool is_complete(){
    		return true;
    	}
    };
    
    class SocketConnection;
    
    class RequestHandler{
    public:
    	virtual ~RequestHandler(){}
    	virtual void handle_request(std::unique_ptr<SocketConnection>&& connection) = 0;
    };
    
    class RequestHandlerImpl : public RequestHandler{
    public:
    	virtual void handle_request(std::unique_ptr<SocketConnection>&& connection){
    		std::cout << "handle_request" << std::endl;
    	}
    };
    
    class SocketConnection{
    public:
    	typedef boost::asio::buffered_write_stream<boost::asio::ip::tcp::socket> stream_buffer_type;
    	SocketConnection(boost::asio::io_service& service) : _data(4096), _stream_buffer(service), _socket(_stream_buffer.next_layer()) {}
    
    	std::vector<char> _data;
    	stream_buffer_type _stream_buffer;
    	boost::asio::ip::tcp::socket& _socket;
    	Request _request;
    };
    
    class Server{
    public:
    	// ###TODO### Fix memory leak for created SocketConnection, when Server is stopped
    
    	Server(unsigned int port) : _io_service(), _acceptor(_io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)){}	
    
    	void read(SocketConnection& socket_connection){
    		std::cout << "read()" << std::endl;
    		socket_connection._socket.async_read_some(boost::asio::buffer(socket_connection._data), boost::bind(&Server::handle_async_read, this, boost::ref(socket_connection), boost::asio::placeholders::error, _2));
    	}
    
    	void handle_async_read(SocketConnection& socket_connection, const boost::system::error_code& error, std::size_t bytes_transferred){
    		std::cout << "handle_async_read()" << std::endl;
    		try{
    			if(error){
    				throw std::string("Error while reading socket");
    			}
    			socket_connection._request.add_info(socket_connection._data, bytes_transferred);
    			if(socket_connection._request.is_complete()){
    				RequestHandler* handler = /* find_request_handler(_request)*/ new RequestHandlerImpl(); /* Nur wegen Vereinfachung ein Memory-Leak */
    				handler->handle_request(std::move(std::unique_ptr<SocketConnection>(&socket_connection))); // <-- HIER: Neun Thread starten, allerdings ist socket_connection nicht copyable, wegen boost::asio::ip::tcp::socket
    			}	
    
    		}
    		catch (...) {
    			delete &socket_connection; // ###TODO### check, if the object can be destroyed
    			throw; // ###TODO### Fliegt noch bis unten durch
    		}
    	}
    
    	void handle_accept(SocketConnection& socket_connection, const boost::system::error_code& error){
    		std::cout << "handle_accept()" << std::endl;
    		SocketConnection* new_socket = new SocketConnection(_io_service);
    		read(socket_connection);
    		_acceptor.async_accept(new_socket->_socket, boost::bind(&Server::handle_accept, this, boost::ref(*new_socket), boost::asio::placeholders::error));
    	}
    
    	void start(){
    		std::cout << "start()" << std::endl;
    		SocketConnection* socket_connection = new SocketConnection(_io_service);
    		_acceptor.async_accept(socket_connection->_socket, boost::bind(&Server::handle_accept, this, boost::ref(*socket_connection), boost::asio::placeholders::error));
    		_io_service.run();
    	}
    
    	boost::asio::io_service _io_service;
    	boost::asio::ip::tcp::acceptor _acceptor;
    };
    
    int main(){
    	Server server(3820);
    	server.start();
    	while(true); // Natürlich nur wegen Vereinfachung
    }
    

    Gruß,
    XSpille

    EDIT:
    Diese Zeile hat mich dazu gebracht einen boost::shared_ptr zu verwenden, obwohl ich eigentlich ein Gegner davon, da er zu oft verwendet wird, obwohl er nicht (zwangsläufig) notwendig ist.
    Ich glaube inzwischen hier ist er jedoch ratsam...

    // ###TODO### Fix memory leak for created SocketConnection, when Server is stopped
    


  • Auweia! Das ist ja ein ganz schönes Durcheinander da.

    Hier mal ein new . Das erzeugte Objekt per Referenz weitergeben. Wieder einen Zeiger holen und den Besitz zwischenzeitlich an ein temporäres unique_ptr-Objekt übergeben (welcher es dann auch zerstört) und dann später nochmal über die Referenze deleten.

    Das Design ist nicht wirklich gut durchdacht bzgl Besitzverhältnisse und das mit den Rvalue-Referenzen und dem std::move hat da auch nichts zu suchen.

    Sowas wie

    try {
      blah
    } catch (...) {
      delete soundwo;
      throw
    }
    

    ist schonmal ein Antipattern. Dafür gibt es ja Dinge wie unique_ptr.

    Versuch Dir Objekt-Besitzverhältnisse als Wald aus Bäumen oder zumindest gerichteten Graphen vorzustellen, wobei die Wurzeln immer irgendwo im Stack liegen. Wenn Du das so organisierst und einem per new Erzeugten Objekt sofort einem anderen Besitzer zuordnest, dann kann eigentlich nicht viel schief gehen. Da Du schon C++0x Mittel verwendest, kannst Du ja std::unique_ptr verwenden, um bei Funktionsaufrufen den Besitz zu übergeben (das ist dann auch selbstdokumentierend) oder eben als roher Zeiger, wobei ich einen rohen Zeiger immer nur dann nehmen würde, wenn der Besitz nicht weitergegeben wird, sondern der Zeiger nur "verliehen" wird.

    Was meine ich mit Besitz? Nun, Grundprizip von RAII ist es, die Verantwortung für Recourcen-Verwaltung an Objekte abzugeben. Das macht vieles einfacher. Und das wird dann oft "Besitz" genannt. Beispiel: Ein vector<double> "besitzt" alle seine double-Objekte. Ein unique_ptr "besitzt" das Objekt, auf das er zeigt. Solange klar ist, wer wofür zuständig ist, ist alles gut. Wenn alle Objekte, die nicht im automatischen Speicherbereich (Stack) leben, einen solchen Besitzer haben, muss man sich auch nicht mehr überlegen, wann wo was manuell gelöscht werden soll.

    Habe ich schon das Zauberwort RAII verwendet? 🙂

    kk



  • krümelkacker schrieb:

    Auweia! Das ist ja ein ganz schönes Durcheinander da.

    🙄

    krümelkacker schrieb:

    Hier mal ein new . Das erzeugte Objekt per Referenz weitergeben.

    Was spricht dagegen? Das Objekt gehört die ganze Zeit dem Server, bis er den Besitz deutlich durch std::unique_ptr abgibt.

    krümelkacker schrieb:

    Wieder einen Zeiger holen und den Besitz zwischenzeitlich an ein temporäres unique_ptr-Objekt übergeben (welcher es dann auch zerstört)

    Genau hier wird der Besitz abgegeben...

    krümelkacker schrieb:

    und dann später nochmal über die Referenze deleten.

    Das delete wird nur bei einer Exception aufgerufen. Wenn die Behandlung des Request in einem anderen Thread abläuft, kann ein Doppel-delete nicht auftreten.

    krümelkacker schrieb:

    Das Design ist nicht wirklich gut durchdacht bzgl Besitzverhältnisse

    Ich habe nur zweimal new in meinem Code. Wozu gehört denn die SocketConnection?
    Bei mir ist der Server solange verantwortlich, bis er sie mit std::unique_ptr explizit weitergibt. In meinem nicht reduzierten Code habe ich noch eine Liste mit allen SocketConnections.

    krümelkacker schrieb:

    und das mit den Rvalue-Referenzen und dem std::move hat da auch nichts zu suchen.

    Sowas wie

    try {
      blah
    } catch (...) {
      delete soundwo;
      throw
    }
    

    ist schonmal ein Antipattern. Dafür gibt es ja Dinge wie unique_ptr.

    Wenn ich nen neuen Thread starte, der damit arbeitet, wie ich vor habe, zieh ich mir das Objekt unterm hintern weg... Wenn der Request noch nicht vollständig ist, wird die Funktion ja nochmal asynchron aufgerufen. Folglich auch dann kann ich es nicht löschen.

    krümelkacker schrieb:

    Da Du schon C++0x Mittel verwendest, kannst Du ja std::unique_ptr verwenden, um bei Funktionsaufrufen den Besitz zu übergeben (das ist dann auch selbstdokumentierend) oder eben als roher Zeiger, wobei ich einen rohen Zeiger immer nur dann nehmen würde, wenn der Besitz nicht weitergegeben wird, sondern der Zeiger nur "verliehen" wird.

    Genauso habe ich es vor...

    krümelkacker schrieb:

    Habe ich schon das Zauberwort RAII verwendet? 🙂

    Ich versuche immer nach RAII zu proggn... Wie gesagt... Ich habe versucht den Code zu reduzieren und meine Liste mit SocketConnections wegreduziert.

    Und wenn ich nen neuen Thread starte muss ich den Besitz ja übergeben (oder kopieren), oder?

    DANKE,
    XSpille


Anmelden zum Antworten