Contents
boost::signals
very extensively. Actually it’s boost signals2 or boost::signals2
. That’s the one which is safe to use with threads, which is very important when you’re making games.
This article will go through a couple of scenarios on how to use Boost Signals2 with various calling techniques and provide several boost signals2 examples for C++ eventing. You can call it an introductory Boost Signals2 tutorial.
The simplest use of signals there is, is to call free methods (functions which are not members of any class). Here is a simple boost signal2 connect example:
#include <stdio.h> #include <boost/signals2.hpp> // Free function 1 void no_arguments() { std::cout << "No arguments" << std::endl; } // Free function 2 void no_arguments_2() { std::cout << "No arguments 2" << std::endl; } int main() { // Shortcut typedefs typedef boost::signals2::signal<void()> registration_manager; typedef registration_manager::slot_type registration_request; // Define the signal handler registration_manager signalHandler; // Connect the above methods to this handler signalHandler.connect( &no_arguments ); signalHandler.connect( &no_arguments_2 ); // Call the handlers signalHandler(); // Output should be: // No arguments // No arguments 2 return 0; }
Now, that isn’t too useful just yet. A free method without any arguments, would either modify the global state or do nothing. Neither is useful and is just ugly.
Next is to pass parameters
#include <stdio.h> #include <boost/signals2.hpp> // Free function 1 void argument(double value) { std::cout << "Arguments " << value << std::endl; } // Free function 2 void argument_2(double value) { std::cout << "Half argument " << value / 2.0 << std::endl; } int main() { // Shortcut typedefs typedef boost::signals2::signal<void(double)> registration_manager; typedef registration_manager::slot_type registration_request; // Define the signal handler registration_manager signalHandler; // Connect the above methods to this handler signalHandler.connect( &argument ); // Defined above signalHandler.connect( &argument_2 ); // Defined above // Call the handlers signalHandler( 3.14 ); // Output should be: // Arguments 3.14 // Half argument 1.57 return 0; }
Lambdas are really easy to use with signals.
#include <stdio.h> // std::cout #include <boost/signals2.hpp> // boost::signals2 int main() { // Shortcut typedefs typedef boost::signals2::signal<void(double)> registration_manager; typedef registration_manager::slot_type registration_request; // Define the signal handler registration_manager signalHandler; // Make a named lambda auto namedLambda = [&]( double value ) { std::cout << "Lambda input value: " << value << std::endl; }; // Connect the class to this handle, // for this we're using boost::bind signalHandler.connect( namedLambda ); // Named lambda defined above // Connect to inline lambda signalHandler.connect( [&]( double value ) { std::cout << "Inline lambda input value: " << value << std::endl; }); // Defined here // Call the handlers signalHandler( 3.14 ); // Output should be: // Lambda input value: 3.14 // Inline lambda input value: 3.14 return 0; }
Using boost::bind
to call members in classes.
#include <memory> // std::shared_ptr #include <stdio.h> // std::cout #include <boost/signals2.hpp> // boost::signals2 class UtilityClass { public: explicit UtilityClass() { } virtual ~UtilityClass() { } public: void handleSignal( double value ) { std::cout << "Within a class: " << value << std::endl; } void handleHalfValue( double value ) { std::cout << "Half: " << value / 2.0 << std::endl; } }; int main() { // Shortcut typedefs typedef boost::signals2::signal<void(double)> registration_manager; typedef registration_manager::slot_type registration_request; // Define the signal handler registration_manager signalHandler; // Make a class instance. std::unique_ptr is not possible, because we have to // 'share' the pointer in the bind expression below. typedef std::shared_ptr<UtilityClass> UtilityClass_ptr; UtilityClass_ptr utils( new UtilityClass() ); // Connect the class to this handle, // for this we're using boost::bind signalHandler.connect( boost::bind( &UtilityClass::handleSignal, utils, _1) ); // Defined above signalHandler.connect( boost::bind( &UtilityClass::handleHalfValue, utils, _1) ); // Defined above // Call the handlers signalHandler( 3.14 ); // Output should be: // Within a class: 3.14 // Half: 1.57 return 0; }
This is a complex example on how to use boost::signals2 within a message system with boost signals2 disconnect method and messaging template class.
#include <stdio.h> // std::cout #include <boost/signals2.hpp> // boost::signals2 #include <memory> // Typedef typedef typename boost::signals2::connection connection; // MessageId typedef int MessageId; // Base Message class class MessageBase { public: // Just go with the default constructors and destructors explicit MessageBase() {} virtual ~MessageBase() {} public: // Abstract method virtual MessageId getMessageId() const = 0; }; // Templated Message class template<MessageId ID> class Message : public MessageBase { public: explicit Message() : MessageBase() { } virtual ~Message() {} public: MessageId getMessageId() const { return MsgId; } public: // Hooray for C++11 static const MessageId MsgId = ID; }; enum MessageTypes : MessageId { MESSAGE_SET_POSITION, MESSAGE_SET_ROTATION, MESSAGE_ORDINAL }; template<typename REGREQUEST,typename MSG> class regRequest_wrapper { public: typedef REGREQUEST request; typedef MSG msg; public: explicit regRequest_wrapper(const request & req) : m_Request(req) { } ~regRequest_wrapper() { } public: void operator()(const msg &m) { m_Request(m); } private: request m_Request; }; class opWrapper { public: explicit opWrapper() {} virtual ~opWrapper() {} public: virtual void call(const MessageBase &msg) = 0; virtual void disconnect_all_slots() = 0; protected: }; template<typename MSG> class opWrapper_templ: public opWrapper { public: typedef MSG msg; typedef typename boost::signals2::signal<void(const msg &)> registration_manager; typedef typename registration_manager::slot_type registration_request; typedef regRequest_wrapper<registration_request,msg> request_wrapper; typedef typename boost::signals2::connection connection; public: connection connect( const registration_request &req ) { return m_Subscribers.connect( request_wrapper(req) ); } void call(const MessageBase &message) { m_Subscribers(static_cast<const msg&>(message)); } void disconnect_all_slots() { m_Subscribers.disconnect_all_slots(); } private: registration_manager m_Subscribers; }; class MessageHandler { public: // Shortcut typedefs // typedef boost::signals2::signal<void(const MessageBase &)> registration_manager; // typedef registration_manager::slot_type registration_request; // typedef std::unique_ptr<registration_manager> registration_manager_ptr; typedef boost::signals2::connection connection; // Define the signal handlers opWrapper * m_MessageHandlers[MESSAGE_ORDINAL]; public: explicit MessageHandler() { for ( size_t i=0; i<MESSAGE_ORDINAL; ++i ) { m_MessageHandlers[ i ] = nullptr; } } virtual ~MessageHandler() { } public: void operator()( const MessageBase & basemsg ) { handleMessage( basemsg ); } void handleMessage( const MessageBase & basemsg ) { const MessageId ID = basemsg.getMessageId(); // Find handler opWrapper *& handler = m_MessageHandlers[ ID ]; if ( ! handler ) { return; } // Call active handler handler->call(basemsg); } template<typename MSG, typename REQ> connection setHandler(const REQ & req) { typedef opWrapper_templ<MSG> wrapper; // Get message id const MessageId ID = MSG::MsgId; // Find handler opWrapper *& ptr = m_MessageHandlers[ ID ]; if( ! ptr ) { ptr = new wrapper(); } wrapper *handler = static_cast<wrapper*>(ptr); return handler->connect( req ); } }; class SetPositionMessage: public Message<MESSAGE_SET_POSITION> { public: explicit SetPositionMessage() {} virtual ~SetPositionMessage() {} }; class SetRotationMessage: public Message<MESSAGE_SET_ROTATION> { public: explicit SetRotationMessage() {} virtual ~SetRotationMessage() {} }; void handleSetPositionMessage( const SetPositionMessage & setposmsg ) { std::cout << "Set position message arrived!" << std::endl; } void handleSetRotationMessage(const SetRotationMessage & setposmsg) { std::cout << "Set rotation message arrived!" << std::endl; } int main() { // Only a message handler MessageHandler handler; // Set handler for SetPositionMessage handler.setHandler<SetPositionMessage>( &handleSetPositionMessage ); // Set handler for SetRotationMessage handler.setHandler<SetRotationMessage>( &handleSetRotationMessage ); // Call the message handler handler( SetPositionMessage() ); handler( SetRotationMessage() ); // Output should be: // Set position message arrived! // Set rotation message arrived! return 0; }
Professional Software Developer, doing mostly C++. Connect with Kent on Twitter.