I understand why I am getting the error I am getting (pure virtual function called). I am trying to call pure virtual functions from within the destructor of my base class shown below. However, I do not know how to rework my code to prevent this from happening. Here are the base and derived classes (the relevant portions anyway):
Base class:
TailFileManager::TailFileManager(const std::string &filename, const int fileOpenPeriod_ms)
: m_Stop(false)
{
m_WorkerThread.reset(new boost::thread(boost::bind(&TailFileManager::TailFile, this, filename, fileOpenPeriod_ms)));
}
TailFileManager::~TailFileManager()
{
m_Stop = true;
m_WorkerThread->join();
}
void TailFileManager::TailFile(const std::string &filename, const int fileOpenPeriod_ms)
{
std::ifstream ifs(filename.c_str());
while (! ifs.is_open())
{
boost::this_thread::sleep(boost::posix_time::milliseconds(fileOpenPeriod_ms));
ifs.open(filename.c_str());
}
ifs.seekg(0, std::ios::end);
while (! m_Stop)
{
ifs.clear();
std::string line;
while (std::getline(ifs, line))
{
OnLineAdded(line);
}
OnEndOfFile();
}
ifs.close();
}
Derived class:
ETSLogTailFileManager::ETSLogTailFileManager(const std::string &filename, const int heartbeatPeriod_ms)
: TailFileManager(filename, heartbeatPeriod_ms),
m_HeartbeatPeriod_ms(heartbeatPeriod_ms),
m_FoundInboundMessage(false),
m_TimeOfLastActivity(0)
{
}
ETSLogTailFileManager::~ETSLogTailFileManager()
{
}
void ETSLogTailFileManager::OnLineAdded(const std::string &line)
{
// do stuff...
}
void ETSLogTailFileManager::OnEndOfFile()
{
// do stuff...
}
You write,
And the code in question is
Happily in a single-threaded execution this couldn't possibly call a pure virtual function. But the thread that you're
join
ing might call a pure virtual function on this object, possibly via a non-virtual member function. If so, then the issue is with the threading, specifically the lifetime management of this object.Unfortunately you do not show the relevant code. Try to reduce things to a small, complete, working example. Where "working" in the sense that it reproduces the problem.
As far as the C++ standard is concerned:
__cxa_pure_virtual
is called.So, you have a bit of a thorny issue...
A possible solution to the problem would be to break it down in two parts, in order to break the destruction in two parts. This could be achieved with a
Strategy
pattern:Let's make it clearer:
The trick ? At the point where
finalize()
is called the destruction of_interface
has not begun yet! The call to the destructor will happen later; and thus you do not suffer from a half-dead object's fate.I'll end this answer by a warning about
join
-ing a thread in a destructor now. Beware that destructors are automatically called in case of stack unwinding, it might therefore be dangerous to wait indefinitely while failing; especially if the thread is waiting for data that should be provided by the currently being unwound one... a classic case of dead-lock.References (n3337):
You shouldn't call virtual functions during construction or destruction, because the calls won't do what you think, and if they did, you'd still be unhappy. If you're a recovering Java or C# programmer, pay close attention to this Item, because this is a place where those languages zig, while C++ zags.
Re-work your design i.e you may call some cleanup function before object get destroyed, idea is just avoid virtual function during const/dest (if there are any!), if you are working with C++...
The rules for virtual invocation are different. C++ 2003, section 12.7 "Construction and Destruction", says:
Lets refresh some old memories ...
Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructorâs own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructorâs class, or overriding it in one of the other base classes of the most derived object (1.8). If the virtual function call uses an explicit class member access (5.2.5) and the object-expression refers to the object under construction or destruction but its type is neither the constructor or destructorâs own class or one of its bases, the result of the call is undefined.
Because of this difference in behavior, it is recommended that you never invoke an object's virtual function while it is being constructed or destroyed.
Never Call Virtual Functions during Construction or Destruction An Excerpt from Effective C++, Third Edition by Scott Meyers June 6, 2005
http://www.artima.com/cppsource/nevercall.html