Reputation: 1621
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...
}
Upvotes: 4
Views: 46592
Reputation: 7768
Depending on what system you're on this may work:
Set a breakpoint on the pure function call handler: __cxa_pure_virtual
.
This worked for me using gdb: break __cxa_pure_virtual
then, when the breakpoint was hit, up
showed my code that was busted. (In my case, I am introducing a second thread and destruction was currently happening on a different thread while the object was being used.)
Documentation of __cxa_pure_virtual
: https://www.swag.uwaterloo.ca/acd/docs/ItaniumC++ABI.htm#pure-virtual
Upvotes: 0
Reputation: 17928
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
Upvotes: 13
Reputation: 299930
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:
class Interface {
public:
friend class Manager;
private:
virtual void finalize() = 0;
}; // class Interface
class Manager {
public:
explicit Manager(std::unique_ptr<Interface>&&);
~Manager();
private:
std::unique_ptr<Interface> _interface;
}; // class Manager
Manager::~Manager() {
_interface->finalize();
}
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):
§12.7/4 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 or from a destructor, including during the construction or destruction of the class’s non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor’s or destructor’s class and not one overriding it in a more-derived class.
§10.4/6 Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.
Upvotes: 7
Reputation: 145279
You write,
“I am trying to call pure virtual functions from within the destructor of my base class shown below.”
And the code in question is
TailFileManager::~TailFileManager()
{
m_Stop = true;
m_WorkerThread->join();
}
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.
Upvotes: 5