knowledge-database (beta)

Current group: comp.soft-sys.ace

[ace-users] C++NPv2 display_logfile.cpp patch

[ace-users] C++NPv2 display_logfile.cpp patch  
David Hawkins
From:David Hawkins
Subject:[ace-users] C++NPv2 display_logfile.cpp patch
Date:23 Jan 2005 00:55:17 -0600
To: ace-bugs@cs.wustl.edu
Subject: C++NPv2 display_logfile.cpp patch

ACE VERSION: 5.3b, 5.4

HOST MACHINE and OPERATING SYSTEM:
Intel/Linux RH9.0

TARGET MACHINE and OPERATING SYSTEM, if different from HOST:
COMPILER NAME AND VERSION (AND PATCHLEVEL):
not applicable

CONTENTS OF $ACE_ROOT/ace/config.h:
not applicable

CONTENTS OF $ACE_ROOT/include/makeinclude/platform_macros.GNU (unless
this isn't used in this case, e.g., with Microsoft Visual C++):
not applicable

AREA/CLASS/EXAMPLE AFFECTED:

$ACE_ROOT/examples/C++NPv2/display_logfile.cpp

DOES THE PROBLEM AFFECT:
COMPILATION?
LINKING?
On Unix systems, did you run make realclean first?
EXECUTION?
OTHER (please specify)?

Other:
Incorrect use of C++
(a) constructor initialization list and
(b) inheritance.

SYNOPSIS:

Incorrect use of C++
(a) constructor initialization list and
(b) inheritance

DESCRIPTION:

(a) Incorrect use of constructor initialization list

The display_logfile.cpp Logrec_Module template, and the
Logrec_Reader_Module class both use the constructor
initialization list to pass a member variable to
the base class. Since the base class construction
will occur before the inherited class construction,
the member variable does not exist at the point that
it is passed to the base-class constructor.

When the derived class construction occurs, the
task_ member variable is initialized (again). The
result of which is probably undefined. The effect
of this sequence is that the task has no idea
that it is part of a module, so none of the
module-specific task member variables that the
base class would have setup eg. the task's module(),
is_reader(), is_writer(), etc are setup.

FIX:
In the derived class constructor, let the base-class
default construction occur and then call
ACE_Module<>::open() in the body of the derived
class constructor.

(b) Incorrect use of inheritance

The inherited modules contained within display_logfile.cpp
are inserted into a stream, and the stream becomes
responsible for deleting the modules. Since the
ACE_Module<> destructor is NOT virtual, when the
stream calls delete through the base class pointer,
the derived module destructor will NOT be called.

Since the example derived modules all contain task
member variables, the destructors for these members
are never called and you have a resource leak.

FIX#1:
As per the examples in the "ACE Programmers Guide",
use inheritance, but do not introduce any new member
variables. Hence, for the display_logfile.cpp example
dynamically allocate the task and set the module
flags such that the baseclass will delete the task
object. The inherited object destructor will still
NOT get called, but I think that this will not
leak resources (but I am not 100% certain).

FIX#2:
Make the ACE_Module<> destructor virtual.
(Is there any reason NOT to do this?)

REPEAT BY:

Read the original example source.

SAMPLE FIX/WORKAROUND:

Here's a patch against the example code from the ACE 5.4
distribution.

--- orig/display_logfile.cpp 2005-01-22 19:10:29.000000000 -0800
+++ work/display_logfile.cpp 2005-01-22 19:10:24.000000000 -0800
@@ -22,15 +22,30 @@
class Logrec_Module : public ACE_Module
{
public:
+ typedef ACE_Module parent;
Logrec_Module (const ACE_TCHAR *name)
- : ACE_Module
- (name,
- &task_, // Initialize writer-side task.
- 0, // Ignore reader-side task.
- 0,
- ACE_Module::M_DELETE_READER) {}
-private:
- TASK task_;
+ // Base-class default construction occurs
+ {
+ // The module deletes the task
+ TASK *task = new TASK;
+ parent::open(name,
+ task, // Initialize writer-side task.
+ 0, // Ignore reader-side task.
+ 0,
+ parent::M_DELETE_WRITER);
+ }
+
+ // WARNING:
+ //
+ // ACE_Module<> does not have a virtual destructor so,
+ // when this class is inserted into a stream as an
+ // ACE_Module<> pointer, it will NOT act polymorphically,
+ // i.e., when the stream calls delete on the ACE_Module<>
+ // pointer, the destructor for this class will not be
+ // called. Because of this, this class can not contain
+ // any member variables. This class serves only as a
+ // convenience to convert a task into a module.
+ //
};
#define LOGREC_MODULE(NAME) \
typedef Logrec_Module NAME##_Module
@@ -195,16 +210,22 @@
class Logrec_Reader_Module : public ACE_Module
{
public:
+ typedef ACE_Module parent;
Logrec_Reader_Module (const ACE_TString &filename)
- : ACE_Module
- (ACE_TEXT ("Logrec Reader"),
- &task_, // Initialize writer-side.
- 0, // Ignore reader-side.
- 0,
- ACE_Module::M_DELETE_READER),
- task_ (filename) {}
-private:
- Logrec_Reader task_;
+ // Base-class default construction occurs
+ {
+ // The module deletes the task
+ Logrec_Reader *task = new Logrec_Reader(filename);
+ parent::open(ACE_TEXT ("Logrec Reader"),
+ task, // Initialize writer-side task.
+ 0, // Ignore reader-side task.
+ 0,
+ parent::M_DELETE_WRITER);
+ }
+
+ // NOTE:
+ //
+ // read the WARNING above in the Logrec_Module template
};

class Logrec_Writer : public ACE_Task
   

Copyright © 2006 knowledge-database   -   All rights reserved