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 _______________________________________________ elfutils-devel mailing list [email protected] https://fedorahosted.org/mailman/listinfo/elfutils-devel
