From: loufoque (mathias.gaunard_at_[hidden])
Date: 2006-10-29 17:51:13
Matt Gruenke wrote:
> Why does an inheritance-based parser need to store objects on the heap?
> If the memory is owned by the parser, it can pre-allocate a temporary
> object for each type of node. Based on the type of node, it fills the
> temporary object of the appropriate type, and returns a const ref to
> that object.
Or better, a boost.variant of those, and we get the reference with the
However in that case the objects are owned by the parser, not the handler.
> If the caller wants a copy, the copy would only have to be
> heap-allocated if copied via some virtual function in the node
> base-class. For the concrete classes (obtained via dynamic-casting),
> copy constructors and assignment operators would work just fine.
> Another option (as you point out) is returning a shared_ptr, though this
> would slightly complicate the parser's management of its temporary objects.
a clone_ptr (smart-pointer with deep-copying, using some template and
virtuality tricks to call the appropriate copy ctor) or poly_obj (the
same but with an object interface instead of a pointer one) could also
be considered instead of shared_ptr.
Those tools were being developed a while ago in boost, I wonder what
happened to them.
Those are the main two ways to wrap polymorphism (where the value is
actually owned): keep value semantics, or make use of the pointers to
avoid copies and give entity semantics, which complicates destruction.
> That could be big, depending on how much text you buffer. Not only
> would it waste memory, but memcpy'ing around all of that could waste
> some of the performance savings gained by avoiding heap allocation.
> Maybe RVO eliminates some or all of the performance penalty, but it's
> probably unwise to depend so much on RVO.
> Of course, passing in the result might be the solution - at least to the
> performance issues. A parser that allows the user to pass in the result
> would also facilitate copying subtrees, if your node type has addChild()
> and addSibling() methods.
I think you should rely on NRVO existing when returning a local variable
Writing void foo(T&); instead of T foo(); is just annoying, and possibly
From what I have tested, NRVO in that case is performed by all modern
compilers. (MSVC6 only performs RVO)
Upcoming move semantics will also allow to prevent copies for sure.
> Why re-invent more than necessary? Use DOM and/or pick some other,
> existing object models (unless you have specific issues which they don't
- DOM has a lot of problems, like the ones in relation to namespaces,
which were added after some time. It's not perfect.
- DOM looks like something made for Java and doesn't make smart use of
what C++ can offer
- DOM requires a lot of memory usage and preprocessing. This is often
not needed, therefore building DOM on top of a lower-level interface is
a better choice
- There is nothing wrong with trying to invent something new if it gives
some interesting benefits over existing solutions. This is what boost
can be seen as: a laboratory where new C++ techniques are experimented.
> I think the biggest reason to avoid exceptions would be for the
> performance impact.
Exceptions should only be thrown when the situation is exceptional, not
when an error is expected.
Therefore they should be thrown very rarely, so the performance impact
is not really relevant. (almost -- implementation dependent)
In our case, though, since we're building a low-level API and expecting
errors, we should not throw exceptions.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk