I've got deep into porting all memory allocation from malloc/free to new/delete.
First a C++ backgrounder -- those who know C++ can skip it.
One exception is char*, since we still use C library string functions and those
don't mix new char[]. strdup() does a malloc(), and you can't free() what you
new'd;
similarly you can't delete what you malloc()d.
While in many cases the code below works, per standard it's undefined behavior:
char * foo = (char*)malloc(100 * sizeof(char));
...
delete foo; // undefined behavior (delete [] foo, the correct thing to do, will
always crash)
char * foo = new char[100];
...
free(foo); // undefined behavior
The use of new/delete is needed to bring functionality from C-style
constructor/free functions to
actual class member constructors/destructors.
The rationale is that C++ handles calling constructors and destructors as
appropriate
when you new and delete class instances (we're talking language constructs, not
Xcircuit
terminology). That way a whole class of errors goes away (failure to initialize
or finalize
data structures), and the code where you use those classes becomes simpler.
C++ also gives you the power of operators, and I've already been able to simply
remove a few copies of list comparison code.
Another goodie that comes from C++: the RTTI (runtime type information system).
A class having any virtual methods has a pointer-size overhead that gives you
dynamic type information. So, in general, when porting from C to C++, you can
drop
"type" fields: the virtual table pointer already does this job.
Here's how one uses it. Suppose you have two classes:
class generic {
public:
virtual ~generic() {} // destructor, does nothing much
}
class derived : public generic {
}
And you instantiate them:
generic * a = new generic;
generic * b = new derived;
Then following hold:
assert(dynamic_cast<generic*>(a) != NULL); // dynamic_cast<type> is like
Xcircuit/glib TO_type(ptr)
assert(dynamic_cast<generic*>(b) != NULL);
assert(dynamic_cast<derived*>(a) == NULL); // equivalent of Xcircuit/glib
style IS_DERIVED(a) == false
assert(dynamic_cast<derived*>(b) != NULL);
And you get not only dynamic casting, but type names and other goodies too:
printf("a's class is %s\n", typeid(*a).name().c_str()); // prints: a's class is
generic
printf("b's class is %s\n", typeid(*b).name().c_str()); // prints: b's class is
derived
End of backgrounder.
I've started with turning all elements into POD (plain old data -- no methods)
C++ classes, deriving from generic.
Then I've added a plist class that contains the parts and plist members. This
class
is used by multiply-inheriting from generic and plist. Here's a path, for
example:
/*----------------------------------------------------------------------*/
/* Path */
/*----------------------------------------------------------------------*/
struct path : public generic, public plist {
u_short style;
float width;
inline path() : generic(PATH) {}
};
typedef path *pathptr;
I've got rid NEW_xxx macros, they are now proper functions:
static inline void NEW_PATH(pathptr* &a, plist *b) {
a = b->append(new path);
}
Those should ideally morph into explicit constructors that take a plist*.
The plist has an append(generic*) method that does what you'd expect it to.
At this point the code compiled and worked.
Then I mechanically went through the code and turned all malloc()s for
non-pointer, non-char memory into news.
Since new type[] is not compatible with realloc(), I'm now pruning all uses of
realloc, by using appropriate container classes from Qt.
A common use of realloc() was to resize point lists. So I made pointlist
a class deriving from QVector<XPoint>, and are in the process of changing
all of the relevant code to compile again. A few pointlist comparisons got wiped
out, since a XPoint has a compiler-defined operator== that does what you expect,
and thus a pointlist's operator== (defined by the QVector<> template class)
also does what you'd expect. That's exactly how C++ saves one lots of
menial, silly work. In some places the pointlist typedef was used where XPoint*
was really meant (a pointer-into-the-list, a.k.a. iterator rather than a list).
Then I'll tackle what remaining uses of realloc() there are.
Finally, free()s will be replaced with delete()s.
That should bring the code back into a working state.
Then I'll move all the constructor/destructor/operator functionality from
detached
C functions into the classes themselves.
Next step: having generic derive from QGraphicsItem, and ensuing fun in pruning
code. Lots of code. Perhaps 10%+ outright in one swoop. If only the nights were
longer :)
QGraphicsItem automatically handles parent-child relationships, so our own
plists
and whatnot will be gone in a blink, etc. But I'm conservative and want to keep
the
code working at the end of each task. Now admittedly there's no testsuite, but
that
can come later. Qt comes with a unit testing framework that can also exercise
the GUI
elements (synthetic keypress/mouse events).
Cheers, Kuba
_______________________________________________
Xcircuit-dev mailing list
[email protected]
http://www.opencircuitdesign.com/mailman/listinfo/xcircuit-dev