I am trying to use the boost::signals2 functionalities in my program. In my "Main Class" called "Solid" I have a member which I initialize inside the constructor body (so after the member initializer list) like this:
pointer_M = boost::make_shared<OtherClass>(/*parameters,...*/);
If the signal is triggered by some function I do not want to call a member of "OtherClass" (to which pointer_M points) but a member from "Solid", i.e. the class which initialized pointer_M just.
I tried the following:
boost::signals2::connection tria_signal = /*...*/.connect( boost::signals2::signal<void()>::slot_type(&Solid::call_this_function, pointer_M.get()).track(pointer_M) );
"call_this_function" is a member function of Solid. Unfortunately there are a bunch of error messages. "OtherClass" and "Solid" are not related by inheritance.
I would appreciated getting some advice how to fix my issue since I am very unexperienced with boost.
Best
But is that what I am trying to achieve possible at all?
The point is we can't tell without a clearer description. It sure sounds like a no-brainer: signals are specifically used to decouple callers and callees.
So let me just make up your "dummycode" for you. I'm going to sidestep the enormous type-overcomplication that you showed in that line:
boost::signals2::connection tria_signal =
/*...*/.connect(boost::signals2::signal<void()>::slot_type(
&Solid::call_this_function, pointer_M.get())
.track(pointer_M));
The whole idea of slots is that they generalize callables using type erasure. Just provide your callable in any compatible form and let the library deduce the arcane implementation types.
#include <boost/signals2.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
struct OtherClass {
OtherClass(...) {}
boost::signals2::signal<void()> tria;
void test() {
if (!tria.empty())
tria();
}
};
struct Solid {
boost::shared_ptr<OtherClass> pointer_M;
Solid() {
pointer_M = boost::make_shared<OtherClass>(1,2,3);
auto tria_signal = pointer_M->tria.connect(
boost::bind(&Solid::call_this_function, this));
}
private:
void call_this_function() {
std::cout << "Solid was here" << std::endl;
};
};
int main() {
Solid s;
s.pointer_M->test();
}
Prints
Solid was here
Maybe we can guess the problem: it looked like you were putting effort into tracking the lifetime of the object pointed to by pointer_M
. That's not useful, since that OtherClass
owns the signal in the first place, meaning that all connections are disconnected anyways when the OtherClass
disappears.
What you likely want is for the connection to disconnect when the lifetime of the Solid
object ends, not the OtherClass
. In general, I'd suggest using scoped_connection
here:
#include <boost/signals2.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
struct OtherClass {
OtherClass(...) {}
boost::signals2::signal<void()> tria;
void test() {
if (!tria.empty())
tria();
}
};
struct Solid {
boost::shared_ptr<OtherClass> pointer_M;
Solid() {
pointer_M = boost::make_shared<OtherClass>(1,2,3);
tria_signal = pointer_M->tria.connect(
boost::bind(&Solid::call_this_function, this));
}
private:
boost::signals2::scoped_connection tria_signal;
void call_this_function() {
std::cout << "Solid was here" << std::endl;
};
};
int main() {
boost::shared_ptr<OtherClass> keep;
{
Solid s;
std::cout << "Testing once:" << std::endl;
s.pointer_M->test();
keep = s.pointer_M; // keep the OtherClass alive
} // destructs Solid s
std::cout << "Testing again:" << std::endl;
keep->test(); // no longer connected, due to scoped_connection
}
Prints
Testing once:
Solid was here
Testing again:
In your case, the OtherClass
is already owned by the Solid
(at least it is created). It seems likely that having the shared-pointer is not necessary here at all:
#include <boost/signals2.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
struct OtherClass {
OtherClass(...) {}
boost::signals2::signal<void()> tria;
void test() {
if (!tria.empty())
tria();
}
};
struct Solid {
Solid() : oc_M(1,2,3) {
tria_signal = oc_M.tria.connect(
boost::bind(&Solid::call_this_function, this));
}
void test() { oc_M.test(); }
private:
OtherClass oc_M;
boost::signals2::scoped_connection tria_signal;
void call_this_function() {
std::cout << "Solid was here" << std::endl;
};
};
int main() {
Solid s;
s.test();
}
Because the members are destructed in reverse order of declaration, this is completely safe.
If you know what you're doing, and the pointer_M
actually needs to be shared, then likely you want to track that pointer. You should probably be considering making Solid
also enable_shared_from_this
. If you want to be really "Enterprise Grade Engineer™" about it, you could perhaps do something fancy with the aliasing constructor: What is shared_ptr's aliasing constructor for?