I have an Event
base class with a DerivedEvent
. I also have BaseA
with DerivedA
and BaseB
with DerivedB
classes. The B
classes have A
objects in them. the A
objects update a queue of event pointers: std::queue<std::unique_ptr<Event>>
from the B
class. There can be multiple A-like
objects in B
. Meaning other derived classes L,M,N,...
that behave somewhat similarly to A
.
In my application, I need to store a history of B
objects. And I need each object in the container of B
objects to be able to .do_stuff()
after an .update()
which should update the event queue. The .update()
method can be called from A,L,M,...
classes that are members of B
. The event queue can reset every time a new instance is re-assigned.
I have followed the cloning ideas from the following question: Copy constructor for a class with unique_ptr. Everything seems to function properly with the exception of the event queue.
Below is a minimal reproducible example:
#include <iostream>
#include <memory>
#include <queue>
class Event{
public:
Event(int event_type): m_type{ event_type } {}
const int get_event_type() const{ return m_type; }
const virtual std::string print() const = 0;
virtual ~Event() = default;
private:
const int m_type;
};
class DerivedEvent : public Event{
public:
DerivedEvent(): Event{0}{}
const std::string print() const override {
return "0 print implementation";
}
};
class BaseA{
public:
virtual bool update() = 0;
// this is a more complicated object than int
virtual int get_value() const = 0;
// provide cloning ability
auto clone() const { return std::unique_ptr<BaseA>(clone_impl()); }
virtual ~BaseA() = default;
protected:
virtual BaseA* clone_impl() const = 0;
};
class DerivedA : public BaseA{
public:
DerivedA(std::queue<std::unique_ptr<Event>>& event_queue)
: m_event_queue{ event_queue }
, m_continue { true }
, m_value { 0 }
{
}
bool update() override {
if(!m_continue){ return false; }
m_value++;
m_event_queue.push(std::make_unique<DerivedEvent>());
return true;
}
int get_value() const override{
return m_value;
}
protected:
virtual DerivedA* clone_impl() const override {
return new DerivedA(*this);
}
private:
bool m_continue;
int m_value;
std::queue<std::unique_ptr<Event>>& m_event_queue;
};
class BaseB{
public:
void do_stuff(){
while(m_A->get_value() < 5){
if(!m_A->update()){
break;
}
// do stuff
std::cout << "doing stuff with object: " << m_A->get_value() << '\n';
// process event queue
while(!m_event_queue.empty()){
std::cout << "processing events\n";
auto event{ std::move(m_event_queue.front()) };
std::cout << "event type: " << event->get_event_type() << '\n';
std::cout << "event print: " << event->print() << '\n';
m_event_queue.pop();
}
}
std::cout << "end of while loop\n";
return;
}
auto clone() const { return std::unique_ptr<BaseB>(clone_impl()); }
virtual ~BaseB() = default;
protected:
std::unique_ptr<BaseA> m_A;
std::queue<std::unique_ptr<Event>> m_event_queue;
virtual BaseB* clone_impl() const = 0;
};
template<class T>
class DerivedB : public BaseB{
public:
DerivedB()
{
m_A = std::make_unique<T>(m_event_queue);
}
// cloning
DerivedB(DerivedB const& other)
{
this->m_A = other.m_A->clone();
}
protected:
virtual DerivedB<T>* clone_impl() const override {
return new DerivedB<T>(*this);
}
};
and my main function:
int main(){
std::unique_ptr<BaseB> b;
// assign unique ptr b to Derived B that has a Derived A member variable
b = std::make_unique<DerivedB<DerivedA>>();
// this behaves properly
b->do_stuff();
// same thing as before
std::unique_ptr<BaseB> test;
test = std::make_unique<DerivedB<DerivedA>>();
// except now keep a vector of B pointers
std::vector<std::unique_ptr<BaseB>> test_history;
// this prints out the "doing stuff with object: 1,...5"
// but it does not have any events to process
auto test_clone { test->clone() };
test_clone->do_stuff();
// this time add test clone to test_history
test_history.push_back(test->clone());
// re-assign test from the history vector
test = std::move(test_history.back());
// remove the last entry from test_history
//test_history.pop_back();
// do stuff --- this fails with value = 2, and doesn't seem to push to m_event_queue
test->do_stuff();
return 0;
}
The problem must be with .clone()
and m_event_queue
. The goal is to be able to have the B
object pulled from the vector test->do_stuff()
to do the same thing as b->do_stuff()
Expected Output ( works with no cloning )
doing stuff with object: 1
processing events
event type: 0
event print: 0 print implementation
doing stuff with object: 2
processing events
event type: 0
event print: 0 print implementation
doing stuff with object: 3
processing events
event type: 0
event print: 0 print implementation
doing stuff with object: 4
processing events
event type: 0
event print: 0 print implementation
doing stuff with object: 5
processing events
event type: 0
event print: 0 print implementation
end of while loop
Output after cloning:
doing stuff with object: 1
doing stuff with object: 2
doing stuff with object: 3
doing stuff with object: 4
doing stuff with object: 5
end of while loop
the m_event_queue
does not get updated in the B
clone object
The reference to
eventQueue
in the clonedDerivedA
is a reference to the originalBaseB
's eventQueue, when the original is destroyed bytest = std::move(test_history.back());
the reference becomes invalid.I'm not sure how you could fix this, it probably depends on your actual use case.