I have a set of classes look like this:
class A {
public:
std::unique_ptr<B> b;
void triggerAsynchronously() {
// this work is submitted to a queue.
b->getC()->signalAsync();
}
};
class B {
public:
std::shared_ptr<C> c;
std::shared_ptr<C> getC() const {
return c;
}
void doSomethingWithSignalFromClassC() {}
};
class C {
public:
void signalAsync() {
// This function is submitted to a queue, and wait until its turn to be executed.
// I want to trigger A::b::doSomethingWithSignalFromC() once this function is done (aka end of this block)
}
}
C::signalAsync()
is triggered by A::triggerSignalCAsynchronously()
. Note that A::triggerSignalCAsynchronously()
returns immediately after submitting works to the queue, but C::signalAsync()
is not executed immediately. Once C::signalAsync()
is executed and done with its work, it should notify A::b::doSomethingWithSignalFromClassC()
.
My question is: how to notify B
when C::signalAsync()
has already finished? From A
, we can get a shared_pointer<C>
. Therefore, natural approach is to store weak_ptr<B>
inside C
. However, this does not work as b
is a unique pointer. I also think about storing a raw pointer of B
inside C, but that seems to go against the unique_ptr
design.
Also, storing a raw pointer to A
in C
seems to be bad:
class A {
void setA() {
b->getC()->setA(this);
}
}
I have the feeling that something is wrong with my design. If it were you to solve this problem, which directions would you try?
CodePudding user response:
You need to be very precise with what you are asking for. You say
Once C::signalAsync() is executed and done with its work, it should notify A::b::doSomethingWithSignalFromClassC()
'Notify' implies that a thread is waiting somewhere to be woken up to do something - this is complex
But you could also mean, I want singalAsync to call a method on an instance of B. This is simple.
Not knowing the constraints on these calls (do you wont then all or are some in libraries that you cant change etc) makes it harder.
Here is what I would do for the callback case
class C {
std::shared_ptr<B> callback_;
public:
C(std::shared_ptr<B> &cb){
callback_ = cb;
}
void signalAsync() {
// noodle noodle noodle
callback_->doSomethingWithSignalFromClassC();
}
}
ie when I construct a C object tell it the obbject to call back on.
Alternatively you could pass that as an arg to signalAsync but maybe thats not possible
Another idea would be to pass a std::function
in the constructor so you dont have to hard code whcih method gets called at the end of signalAsync
CodePudding user response:
According to pm100's answer, the usage of std::function
actually works. I want to write down what I do in the end, just in case anyone might need it. A little bit more details, in my case, C
's header file cannot include B
's header file because B
already includes C
in its header (forward declaration not work). C
also could not include A
because A
is designed to be an Implementation class (Pointer to Implementation idiom); therefore, A
header is inside src
folder and should not be included anywhere (except for A.cc
)
What I did in the end:
class A {
public:
std::unique_ptr<B> b;
void triggerAsynchronously() {
// this work is submitted to a queue.
b->getC()->signalAsync();
}
void setFunctionToC() {
// This happens before triggerAsynchronously()
auto do_something_when_finished = [&b = b]() {
b->doSomethingWithSignalFromClassC();
}
b->getC()->setFunctionToC(do_something_when_finished);
}
};
class C {
std::function<void()> call_on_finished;
public:
void setFunctionToC(std::function<void()> f) {
call_on_finished = f;
}
void signalAsync() {
// This function is submitted to a queue, and wait until its turn to be executed.
// I want to trigger A::b::doSomethingWithSignalFromC() once this function is done (aka end of this block)
call_on_finished();
}
}