OldRalle
OldRalle

Reputation: 21

boost:signals2: use a classes public signal in its constructor

In my private project I use boost::signals2 to send messages from several classes to a common receiver. My Question is: how can I send messages from the constructor of a class? The problem is, that the signal in this case does not exist until an object of class A is instantiated (until the constructor is executed). But at this point there was not yet a chance for some other code to establish a slot.

I tried to use static boost::signals2::signal<void(const std::string & Msg)> but then I get loader errors 'undefined reference to class::signal'.

Below is a very stripped down version of my code for illustration purposes. The question is: how can class B receive a message from the constructor of class A? Or with other words: how can I connect a slot to the signal of class A before the constructor of class A is executed?

class A { 
public:
   /*static*/ boost::signals2::signal<void(const std::string & Msg)> sigA;
   A() { sigA("message from constructor A"); }
};

class B { 
   B() {
      A a;
      a.sigA.connect([](const std::string & msg) { std::cerr << msg << '\n'; } );
   }   
};

For your information: I use fedora 33, gcc 11.2.1 and C++17.

Please excuse my english: it's not my native language und i am missing some experience...

Upvotes: 1

Views: 275

Answers (2)

OldRalle
OldRalle

Reputation: 21

Meanwhile I found a solution for my problem by myself. Below is a fully working example of how the constructor of class A fires a signal that itself is a (static) member of class A:

#include <string>
#include <iostream>
#include <functional>
#include <boost/signals2/signal.hpp>

class A { 
public:
   using Signal = boost::signals2::signal<void(const std::string & Msg)>;
   static Signal sigA;

   A() { sigA("message from constructor A"); }
};

A::Signal A::sigA;

class B { 
public:
   B() {}
   void disp(const std::string & msg) { std::cout << msg << '\n'; }
};

int main()
{
   B b; // slot b.disp() now exists
   auto slot = std::bind(&B::disp, b, std::placeholders::_1);
   A::sigA.connect(slot); // subscribe slot b.disp() to A::sigA
   A a; // constructor of class A fires signal sigA, b.disp() writes to cout
};

Upvotes: 1

Elad Maimoni
Elad Maimoni

Reputation: 4595

Technically, you could pass the slot to class A constructor.

class A { 
public:
   A(boost::signals2::slot<void(const std::string &)> slot) { 
   sigA.connect(slot);
   sigA("message from constructor A"); }
};

class B { 
   B() {
      A a([](const std::string & msg) { std::cerr << msg << '\n'; });
   }   
}; 

Note that the semantics are a bit weird. For once, there is no way to obtain the connection object. you could use an out parameter on the constructor, but that really is a code smell.

Generally, constructors are used to initialize the object, not to perform logic that might affect objects that are not class members.

I would strongly suggest subscribing the slot in a separate method.

Upvotes: 1

Related Questions