This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: dwarf_data value


Seeing the subject, I thought this was going to be a question about the
value of having the dwarf_data base class.  But now I see it's actually
about dwarf_data::value.

> These are mostly just notes to myself. But hints to the ideas behind the
> abstracts always appreciated (I might just not be up to c++ design
> patterns, so please feel free to just tell me to lookup some
> foo-creator-bar if things are just standard). 

Nobody has accused me of following any C++ norms.  I just like composition.

> Hoping to make the dwarf_output copier less abstract I am trying to
> better understand the ideas behind dwarf_data values which seem the base
> of every value (in dwarf_edit and dwarf_output). 

There aren't too many deep ideas there, just a lot of layers of modularity
hair.

> Where attr_values template classes use specific implementations as
> template<class impl, typename vw = value<impl> >.

Right.  dwarf_data is the shared base class of dwarf_edit and dwarf_output.
In dwarf_edit, everything is its own mutable cell.  In dwarf_output,
everything is an immutable pointer into a value_set in the collector.
So all this stuff is just the modularity interfaces for sharing between
those two uses, and the common parts that have to do with the dynamic
typing dispatch of attribute values.

> A elfutils::dwarf_data::value is defined as a
> template<typename impl, bool alloc_values = true>

Right.  impl is dwarf_edit or dwarf_output.  For dwarf_edit, each
attr_value cell contains its own new'd pointer (hence, alloc_values) that
is delete'd normally.  For dwarf_output, all the pointers come from the
collector and we never delete them directly.

> It defines a struct value_dispatch with just a virtual destructor and a
> typedef value_dispatch value_cell_type;

This is the base type of all the value_* types, one for each value space.
(Actually, there are two types for VS_constant, since we distinguish small
constants and constant blocks for the typing, but not for the value spaces.)

> It has two template variant functions:
>
>       template<typename flavor>
>       static inline flavor &
>       variant (flavor *&, const value_dispatch *&)

Note this is for getting a const parameter.  
That's what's not supposed to happen.

>       template<typename flavor>
>       static inline flavor &
>       variant (flavor *&result, value_dispatch *&value)
> 
> The first just throws because it "can't happen!" (because the . The
> second will either create a new flavor and return a pointer to it when
> the given value is NULL. Or it will dynamic_cast the value to flavor and
> return a pointer to that. The result argument reference will be used in
> both cases to store the return value.

This is used for dwarf_edit.  Its only use is in attr_value::variant, the
non-const overload.  A dwarf_output::attr_value is always const when used,
so attr_value::const_variant is what really gets used.

> A maker struct template and a make template function are provided that
> returns a maker struct instantiated for the arg_type. The maker struct
> just provides a templated function make that will create a new flavor
> from input x and arg_type arg to be assigned to a value dispatch pointer
> and flavor result pointer.

Right.  The result parameter is actually just there to get the template
overloading to happen right.

> All the above seems plumbing for casting stuff around, although I am not
> really sure why and what it is to achieve. The values seem designed to
> be used as is for dwarf_edit (default alloc_values = true). dwarf_output
> overrides maker and make.

Right.  TBH, I don't really remember the exact reasons for all the
contortions.  I'm just fairly sure that if you take some out, the stuff
will stop compiling.  Making the templates happy for these two uses got
very hairy.  We can try to clean it up if you find things that work.  But
it's indeed all just C++ magic to make the template and type systems happy,
and it should all basically compile away.

The real data structures are relatively simple.  An attribute map has an
int lhs and an attr_value rhs.  An attr_value is just a value_dispatch
pointer.  In dwarf_output, it's a const value_dispatch pointer that points
into some set maintained in a dwarf_output_collector.  In dwarf_edit, it's
a simple pointer with new/delete (attr_value destructor calls delete).

> The rest of the structs defined by dwarf_data::value are much simpler
> representations of various dwarf entities. All extend value_dispatch.

Right.  We use the dynamic subtype to record which value_space each
attribute has.

>   value_string (also extends std::string)
>     value_identifier

You'll note these have entirely the same implementation.
They are distinct types only because they are distinct value spaces.
See data-values.hh, where what_space is implemented.

>   value_source_line
>   value_source_column
>     (currently empty)

It extends value_source_line.  Again, the data is the same (just the int),
but the value spaces differ.

>   value_macptr
>     (currently empty)

This is indeed just a stub, because we haven't implemented any C++ APIs for
the .debug_macinfo stuff yet.

> All constructors are templated on arg_type, but I don't understand
> how/why this is used.

The constructors take two arguments: a corresponding thing to copy, and an
arg_type.  The second argument serves two purposes.  First, it makes
doubly-sure that we never get to this code via implicit copy constructor
calls.  (It can get tricky to figure out when a temporary object with copy
construction might be implicit in some hairy C++ expression you used.)
There is also the 'explicit' keyword, but this is even better, because
there is no way even an "explicit" copy construction can get to these
methods.  The default arg_type is subr::nothing, which is a named empty
struct.  That's what dwarf_edit uses.  The empty object argument gets
inlined and optimized away entirely, but for the typing it ensures we're
doing what we mean to.

The second, and crucial purpose of the second argument is that this is how
the dwarf_output::copier reference gets passed around.  It has to be a
template type because there is a different dwarf_output::copier<impl> type
depending on which class you are copying from.  All the various levels of
constructors pass the copier reference down.  Eventually it gets down to
dwarf_output::value::maker, where we use overloading on the value_* types
to choose the right add_* call into the collector.

This is where all the action is, for non-reference attribute values.
(For reference attributes, this punts back to copier::add_reference,
which is the entry into that whole world of weirdness.)

The dwarf_output_collector class is not a template type, but its add_*
methods are templates parameterized by their input argument type.  All
the template hooey layers above come down to calling one of these with
whatever value is returned by attr->second.string () and so forth.
Those do the insertion into a value_set, returning the const value_*
pointer into the now-permanent, single copy of that identical value held
in the collector.  These pointers are what wind up in _m_value cells of
the attr_value objects inside attribute maps.

The copier add_source_file, add_string, and add_identifier methods
actually use their own local layer of caching before calling into
the collector set-insertion methods.  That should be more or less
self-explanatory looking at copier::string_cache.


Thanks,
Roland

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]