I have recently been messing around with event systems in C++ like boost::signals2 and similar libraries. They all seem to have one limitation in common: they don't really work with move semantics.
If you connect your signal to a slot that is a member of an object it refers to the current memory address of that object to call the slot. Once that address changes, for example because the std::vector the object is in has to reallocate it's memory array, the connection essentially breaks. It still refers to the moved-from address.
I am a little bit confused on how to correctly use such signal/slot libraries. Do you really just have to make sure the slot doesn't never changes it's location, for example by placing it on the heap? Or is there a way to make the signal automatically be aware of the changed slot location?
The thing is that slot's location in memory doesn't depend on instances data. Member function exists in memory as single definition for all instances of the class and doesn't change its location. When you call the function for specific object (like object->member_func()), "this" pointer is implicitly passed among with other args, so the function knows which object it is called for. https://www.tutorialspoint.com/cplusplus/cpp_this_pointer.htm
Here is a simple example of boost signals2 signal usage:
class Handler
{
private:
std::vector<std::string> array;
public:
void member_func(int value) {}
};
#include <boost/signals2.hpp>
void test()
{
boost::signals2::signal<void(int)> signal;
Handler object;
signal.connect(std::bind( &Handler::member_func, &object, std::placeholders::_1 ));
signal(5);
}
We pass pointer to the object (&object) in bind method args, so when the signal will be invoked, boost will call object.member_func(); And if you add any data members to Handler class or change them in runtime, that doesn't affect the connections.
The only thing here you need to care is the lifetime of the object: you must disconnect slot before deleting the object. Otherwise, when signal is invoked, object.member_func(); call leads to undefined behavior because the object doesn't exist any more.
Related
When calling C++ from QML, a QObject can be returned to qml by pointer. Before returning, I can call
QObject* qobj = m_sharedPtr.data(); // Pointer to member shared-ptr-managed object.
QQmlEngine::setObjectOwnership(qobj, QQmlEngine::CppOwnership);
return qobj;
but given that QML is garbage-collected, how can this work safely? My mental model is that QML will get the pointer and hold onto it, wrapped in some QML pointer wrapper and that pointer wrapper will eventually be GC'd. But then there's no limit to how long after the setObjectOwnership call that QML could access *qobj. (E.g., perhaps the next QML->C++ call after this one causes m_sharedPtr to go out of scope.) Does that mean QQmlEngine::CppOwnership is only safe to use when the object's lifetime is essentially infinite (e.g., a singleton)? I don't see any alternative, but haven't found any mention of this issue in any documentation.
If m_sharedPtr refers to data created in C++, qobj will have CppOwnership anyways, and you should handle it like any other C++ smart pointer (i.e. let it live as long as you actually need it).
If it was created in QML, it will have JavaScriptOwnership by default. If you then transfer ownership to C++, and it gets destroyed, it's not accessible in QML anymore.
If it is important that qobj is accessible in QML for an uncertain time, but still C++-managed, you could retreat to using raw pointers and handle the destruction yourself (e.g. by connecting to the aboutToClose() signal of a QML window).
I am currently experimenting with writing an event queue in C++11. I am using std::bind to obtain std::function objects which are called when certain events happen. The code for this roughly looks like this:
class A
{
public:
void handle();
};
class B { ... };
// Later on, somewhere else...
std::vector< std::function< void() > functions;
A a;
B b;
functions.push_back( std::bind( &A::handle, &a ) );
functions.push_back( std::bind( &B::handle, &b ) );
// Even later:
for( auto&& f : functions )
f(); // <--- How do I know whether f is still "valid"?
Is there any way to guarantee the validity of the function object so that I can avoid stumbling over undefined behaviour here?
I have already taken a look at this question here, std::function to member function of object and lifetime of object, but it only discussed whether deleting a pointer to a bound object raises undefined behaviour. I am more interested in how to handle the destruction of such an object. Is there any way to detect this?
EDIT: To clarify, I know that I cannot guarantee a lifetime for non-static, non-global objects. It would be sufficient to be notified about their destruction so that the invalid function objects can be removed.
As #Joachim has stated, no lifetime is associated to the member function (it's a code section, not data). So you're asking if there is a way to know if the object still exists prior to execute the callback call.
You've to make a sort of framework, where the object dctor notify the container when it is destroyed, so the container could delete it from its "observers", the vector containing all the objects. To do that, the object must memorize in its instance the ptr to the container.
UPDATE
#Jason talks about the use of shared_ptr. It's okay to use them, but in this case, is not addressing the case of HOW to destroy the object linked in other object-notification list. Shared_ptr postponed the destruction of an instance until all "managed" references to it are deleted. But if you need to destroy object A, AND delete all reference to it because that object MUST be deleted, you've to look into all containers that store a shared_ptr and remove it. A very painful activity. The simplest solution (using raw ptr or shared_ptr, if you can use them, is irrelevant) is a two-link connection between the observer and the observed, in such way each one can notify its destruction to the other. How to store this information? many ways to accomplish it: hash tables, slots in observer, etc
One hack/workaround that achieves the desired result would be to use a parameter of type std::shared_ptr. When the bind is destructed, so is the shared pointer - which will do the right thing when it is the last reference. However this involves changes to the signature used. To make it slightly less awkward, you can use static methods that take in a std::shared_ptr this - sort of like the self parameter concept in python, if you are familiar.
Or if you are fine with C++11, you can just use a lambda capture of the shared pointer.
You'd need to dynamically allocate the instances to use this method.
1)What is the exact difference of the following two ways of creating an object from a class named "handler"
handler myhandler;
handler myhandler=new handler();
2) Is the following alwasy possible?
handler *myhandler;
handler *myhandler=new handler();
handler myhandler;
That creates an object. If it's inside a function, then it has automatic storage duration, and will be destroyed automatically when it goes out of scope. If it's outside a function, then it has static storage duration, and will be destroyed automatically at the end of the program.
handler myhandler=new handler();
That probably doesn't compile; unless handler has a strange constructor allowing implicit conversion from a pointer, in which case it does something strange.
handler *myhandler;
That declares a pointer, which could be used to refer to an object of type handler. It doesn't create an object, nor does it point to any object yet.
handler *myhandler=new handler();
That creates a dynamic object, and initialises a pointer to point to it. This is usually a bad idea, since it's likely to lead to a memory leak (or worse) when you fail to delete it correctly. Don't create dynamic objects unless you really need them to outlive the current scope; and use a smart pointer when you do need to:
auto myhandler = std::make_unique<handler>(); // C++14
std::unique_ptr<handler> myhandler(new handler); // C++11
If you really want to juggle a raw pointer for educational purposes, then remember to delete the object once you've finished with it:
delete myhandler;
and take special care to make sure this only happens once, and happens even if an exception is thrown.
Short answer is that the second method is correct. The first creates a new object and then assigns a new object on top of it. I strongly recommend understanding this before proceeding:
http://www.parashift.com/c++-faq/self-assignment-how.html
Your program should not compile as you are redefining an object in both the case.
Your first case should not compile at new statement as new returns a pointer to the newly created object unless there is a constructor taking pointer or assignment operator overloaded which takes handler pointer.
In the second case, it will point to newly created object.
To correct first case,
handler myhandler=handler();
provided handler has a default constructor or constructor with default arguments.
I want to implement a simple Quake-like console. This implementation is roughly based on the implementation in Doom 3:
class Console
{
public:
...
typedef boost::function<void(const Arguments&)> Callback;
void register_command(const std::string& name, const Callback& callback);
void unregister_command(const std::string& name);
};
However, I plan to support loadable modules that can also be unloaded. When the module is unloaded, it needs to unregister its commands. Unregistring them one by one is tedious and error-prone. How to automate it?
Have your Console::register_command function return an object. This object (or a copy thereof) has one member function: unregister. Calling it will unregister the particular registration it was given.
Therefore, each module can have a std::vector<> of these objects. It can unregister all of them as part of its destructor.
You don't want the object's destructor to do it, unless you make the object like a shared_ptr with reference counting. Or if you're using C++11 and can make the object move-only.
Note that the object will need to reference the Console object it was created from. So you can have lifetime issues, where the Console has been destroyed yet not all of these objects it created are gone. Dealing with this could involve use of shared_from_this, where Console is always stored in a shared_ptr (whether boost::shared_ptr or C++11 std::shared_ptr). The registration objects would have a weak_ptr to the Console, so they can test to see if it is still alive.
could someone summarize in a few succinct words how the boost shared_from_this<>() smart pointer should be used, particularly from the perspective of registering handlers in the io_service using the bind function.
EDIT: Some of the responses have asked for more context. Basically, I'm looking for "gotchas", counter-intuitive behaviour people have observed using this mechanism.
The biggest "gotcha" I've run into is that it's illegal to call shared_from_this from the constructor. This follows directly from the rule that a shared_ptr to the object must exist before you can call shared_from_this.
From my understanding, sometimes in your code you want a class to offer up shared_ptr's to itself so that other parts of your code can obtain shared_ptr's to an object of your class after it has been constructed.
The problem is that if your class just has a shared_ptr<> to itself as a member variable, it will never get automatically destructed, since there is always "one last reference" hanging around to itself. Inheriting from enable_shared_from_this gives your class an automatic method which not only returns a shared_ptr, but only holds a weak shared pointer as a member variable so as not to affect the reference count. This way, your class will be freed as usual when the last reference to it is gone.
I've never used it, but this is my understanding of how it works.
shared_from_this<> is used if an object wants to get access to a shared_ptr<> pointing to itself.
Usually an object only knows about the implicit this pointer, but not about any shared_ptr<> managing it. Also, this cannot easily be converted into a shared_ptr<> that shares ownership with other existing shared_ptr<> instances, so there is no easy way for an object to get a valid shared_ptr<> to itself.
shared_from_this<> can be used to solve this problem. For example:
struct A : boost::enable_shared_from_this<A> {
server *io;
// ...
void register_self() {
io->add_client(shared_from_this());
}
};
the boost::asio::io_service destructor documentation explains it fairly well
The destruction sequence described
above permits programs to simplify
their resource management by using
shared_ptr<>. Where an object's
lifetime is tied to the lifetime of a
connection (or some other sequence of
asynchronous operations), a shared_ptr
to the object would be bound into the
handlers for all asynchronous
operations associated with it. This
works as follows:
When a single connection ends, all associated asynchronous operations
complete. The corresponding handler
objects are destroyed, and all
shared_ptr references to the objects
are destroyed.
To shut down the whole program, the io_service function stop() is called
to terminate any run() calls as soon
as possible. The io_service destructor
defined above destroys all handlers,
causing all shared_ptr references to
all connection objects to be
destroyed.
Typically your objects will chain asynchronous operations where the handlers are bound to member functions using boost::bind and boost::shared_from_this(). There are some examples that use this concept.
Stuff is missing from some of the comments above. Here's an example that helped me:
Boost enable_shared_from_this example
For me, I was struggling with errors about bad weak pointers. You HAVE to allocate your object in a shared_ptr fashion:
class SyncSocket: public boost::enable_shared_from_this<SyncSocket>
And allocate one like this:
boost::shared_ptr<SyncSocket> socket(new SyncSocket);
Then you can do things like:
socket->connect(...);
Lots of examples show you how to use shared_from_this() something like this:
boost::asio::async_read_until(socket, receiveBuffer, haveData,
boost::bind(&SyncSocket::dataReceived, shared_from_this(), boost::asio::placeholders::error));
But was missing for me was using a shared_ptr to allocate the object to begin with.