Boost logo

Boost :

From: Carlo Wood (carlo_at_[hidden])
Date: 2002-08-31 12:21:06


On Fri, Aug 30, 2002 at 01:08:26PM -0400, Rob Stewart wrote:
> Let me see if I understand the problem you're dealing with.
> Because of the possibility of demangle() being called from within
> a call to malloc(), you can't rely on ordinary memory allocation
> routines because of the possibility of a deadlock.
>
> That leads me to the following questions:
>
> - Under what scenario will demangle() be invoked from within a
> call to malloc()?

At this moment I am only aware of one case: libcwd.
In general you could say "memory debuggers" that do demangling
of function names from which an allocation is performed.
Output of libcwd can look like:

MALLOC : operator new[] (size = 50) = <unfinished>
BFD : address 0x804f95f corresponds to alloctag.cc:50
MALLOC : <continued> 0x81c1b38

> - Why would the demangle() client care how demangle() manages its
> memory?

Because demangle() uses the STL :/. I could try to implement
my own containers, but I'd rather just use basic_string and
vector. When I'd just use the default allocator then operator new
will be called which I cannot treat the same way as I'd normally
do at that moment (even apart from a possible deadlock).
I can also not treat it special by setting a flag because it *is*
part of the normal, default, std::allocator memory pool and as
such not distinguisable from other allocations by STL containers.
(For example, I could not store the allocation in an internal
administration of allocations but immedeately return __libc_malloc,
however - then when normal user code is using std::allocator and
at that moment this memory is freed again, the internal administration
would not find it and assume the user is trying to deallocate an
invalid pointer).

> - If demangle() needs a separate memory scheme, couldn't it use
> OS-specific means for establishing a separate heap? I realize
> that implies portability issues, but it would work, wouldn't
> it?

I don't feel that it is worth it. It's not that much of an issue
to just pass an Allocator is it? The only real disadvantage is
it will turn the demangler code into a template library which could
cause several different instantiations to run on the same machine
(as you can't use a template library as a shared library).
But, since indeed this is probably only used by developers while
debugging, that is not a real issue in fact. The second problem
of a template library is that is has to be compiled every time
again (since you need to include it with an #include; well, we
all know the horror of the current <iostream> and <string> causing
60,000 lines to be dragged in - slowing down compilation of all
source file with a factor of 10 to 100 of what it COULD have been
if only we had precompiled headers :(). However - the developer
really only needs to add the demangler include in ONE source file,
and therefore also this is not an issue imho.
Conclusion: I see no real problem to iplement the demangler as
a template library and thus I see no reason not to add the flexibility
of allowing to specify the used allocator.

> - I don't suppose that the information demangle() needs to track
> could be preallocated, could it? (I'm gathering that you want
> to track information per symbol, so the data is unbounded.)

I suppose there is a reasonable limit to what will be needed, but
you can never garantee that any pre-allocation will really be enough
unless you preallocate a megabyte.

> - Didn't you previously mention that there could be an interface
> to supply the (de)allocation functions? That's not unlike
> set_terminate(), but it leads to problems if you need
> demangle() to work before entering main(). So, why not create
> a parameterized class which can be given the required functions
> as part of specializing the type:
>
> template <typename Allocator, typename Deallocator>
> struct demangler
> {
> static std::string type(std::string const & type_i);
> static std::string type(std::type_info const & type_i);
> static std::string symbol(std::string const & symbol_i);
> };
>
> Usage then becomes:
>
> typedef demangler<?, ?> demangle;
> std::cout << demangle::type(mangled_type_name);
> std::cout << demangle::type(T.type_id());
> std::cout << demangle::symbol(mangled_symbol_name);

This is still a template library and thus solves nothing.
But - I like the interface you describe here for passing
the type of the allocator.

We could do:

namespace boost {

  template <typename Allocator>
    struct demangler {
      template <typename InputIterator, typename OutputIterator>
        static OutputIterator type(InputIterator first, InputIterator last, OutputIterator out);
        static OutputIterator symbol(InputIterator first, InputIterator last, OutputIterator out);

        typedef std::basic_string<char, std::char_traits<char>, Allocator> string;
    };
}

And the usage then will be:

// Define your own convenient interface:
std::string demangle_type(std::string const& type_i)
{
  std::string out;
  boost::demangler<std::allocator>::type(type_i.begin(), type_i.end(), out.begin());
  return out;
}

-- 
Carlo Wood <carlo_at_[hidden]>

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk