[Aptitude-devel] Experimental package and package_pool implementation

Piotr Galiszewski piotr at galiszewski.pl
Mon Jul 12 22:57:55 UTC 2010


2010/7/13 Daniel Burrows <dburrows at google.com>:
> On Mon, Jul 12, 2010 at 2:03 PM, Piotr Galiszewski <piotr at galiszewski.pl> wrote:
>> Today I have been designing and writing new packages class
>> implementation. All work is available at 004.1-package branch on
>> gitorious.
>
>  Thanks!  Do you think you could rebase these onto master?  (probably
> renaming them 001.1 then)
>

Will do

>> Firstly I created new package class, This version doesn't contains any
>> reference to any Qt classes. As it was suggested in the discussion,
>> all members of the class are independently lazy loaded from the
>> pkgIterator object. Package name is an only exception, cause it would
>> be loaded for all package shortly after creating package_model. In
>> order to avoid discarding const qualifiers of getters, all class
>> variables are mutable and requires methods are const. I am not using
>> PackageInformation class from GTK frontend, but I have moved required
>> code to this new class. For a convenience I have added a typedef for
>> package's boost::shared_ptr. If the program is compiled with Qt
>> support this pointer is exported as metatype for use in packages_model
>
>  A couple comments here:
>
>    - What's a metatype?
>

Qt models use QVariant type for distributing object. QVariant requires
registered types which have public default constructor, a public copy
constructor, and a public destructor. More informations:
http://doc.qt.nokia.com/4.6/qmetatype.html

>    - Many of the values in this class can vary by version; maybe it would
>      be a good idea to have a "version" class to go with package?
>
>    - In that case, the list of versions should probably be a list of shared
>      version pointers.
>

it will work. Should I do this, and use the same code principle as here?

>    - I would return the members of package by const reference, to avoid
>      extra copies.  This is slightly dangerous, of course, but the idiom is
>      widely used inside aptitude; return by reference generally indicates
>      that you're getting a reference to part of an object and you should
>      copy it if you want it to survive longer than the parent object.
>

Sure, I was sure, that I have forgotten about something important :P

>    - I would probably do this for the list of versions:
>
>          typedef /* TBD */ version_type;
>
>        public:
>          typedef std::list<version_type>::const_iterator version_iterator;
>
>          version_iterator versions_begin();
>          version_iterator versions_end();
>
>      That insulates the client code a bit more from the details of how the
>      versions are stored.
>

OK

>    - Typically I declare "namespace cw = cwidget" at the top of files that use
>      cwidget names, to make names shorter.
>
>    - On namespacing: I've recently taken to pulling individual names in via
>       "using" at the top of the file; it's nice documentation of what a module
>       uses:
>
>           using cw::util::transcode;
>
>       This avoids the problem with "using namespace", where you pollute
>       your namespace with a huge amount of junk.
>
>       I generally don't do this for names in std::, because that's not too long
>       a prefix, and the names in std tend to be fairly generic.  I could be
>       convinced that it's a good idea, though.
>

OK

>    - Is the list of "tags" the debtags for the package?  I believe that tags.h
>      returns those as a std::set<tag>; it would probably make sense to just
>      store that value directly.
>

Yes it is. I planned to add this later

>    - Maybe inherit from boost::noncopyable to make it noncopyable?
>
>    - I would drop the virtual destructor here and make it impossible to
>      subclass package (by hiding its constructor and using a static factory).
>      That allows you to avoid a virtual method table for each instance (not
>      a huge deal even if we create tens of thousands of them, but not bad
>      either)
>

probably it is not an option, because it won't work with QVariant type

>    - I really wish boost::flyweight didn't suck so we could make these strings
>      into flyweights easily.
>
>> Second class is package_aware_object. It purpose is to notify relevant
>> classes about changing a state of package_pool class, which was added
>> in later patch. If some class want to be notified about this event, it
>> needs to inherit from this class and reimplement three methods. This
>> approach is commonly used instead of Qt signals in Kadu IM, so I'd
>> like to ask you about your opinions. package_info_tab and
>> status_widget will be another classes using this aware object
>
>  The normal way of doing this in aptitude would be to just connect to
> the relevant signals out of src/generic/apt.h and src/generic/aptcache.h.
> I don't see a problem with having a convenience base class that sets up
> these connections for you in its constructor.  And using sigc++ will give
> you automatic features like detaching signals when the target is
> destroyed (as long as you inherit from sigc::trackable, which you can do
> in the base class).  [ok, I read your comments below; I'll think this over
> some more]
>
>  You should store the object pointers in a hash table (e.g.,
> boost::unordered_set) so that removing them is efficient.
>
>  Also, the state-changed signal should probably take a set of package
> objects instead of accepting them one at a time.  That's a bit problematic
> since you'll have to implement operator< or a hash function; a list would
> be OK for now.
>

OK. I was little confused about this two signals. I thought that that
this will be better. Will be fixed

>  I do find it odd that the methods to trigger the signals are public, but
> the signals are protected.  If you set up connections to the global signals
> in the base class constructor, you shouldn't need public methods to trigger
> the notifications, and you can encapsulate all the signal-handling in one
> module.  Conversely, I don't see any harm in making these methods
> public.  aptitude generally doesn't use protected inheritance, although this
> is a reasonably sane use of it.
>
>  I know there are a bunch of examples of this in the aptitude source
> base.  e.g., look at how src/gtk/info.cc uses the cache_closed and
> cache_reloaded signals.  The package_states_changed signal is used
> in entityview.cc.
>
>  (an amusing variant would be to use *private* virtual methods here.
> I don't think there's any real point in deploying that particular C++ trick,
> though)
>
>  I know I said that I normally prefer fully abstract interfaces :).  This
> may be an exception -- for this sort of event handling it could be better
> to have default NOP implementations.
>
>> Third patch introduces package_pool. This class is responsible for
>> managing a list of packages, which was earlier held by packages_model
>> class. Updated packages_model was added in fourth patch. packages_pool
>> will be the only class which is directly connected to signals about
>> cache updating or reloading. It updates inner package list (which uses
>> boost::multi_index_conantainer) and later translates this signal and
>> propagates it by package_aware_object. I did it in this way, cause I
>> need to be sure that packages_pool is updated earlier than other
>> classes using package objects.
>
>  First, please put this behind an abstract interface -- it shouldn't be called
> often enough for that to matter performance-wise, and right now you're
> forcing everyone to instantiate a multi_index_container if they include it.
> (that will also help with unit tests -- see below)
>  Similarly, you should be able to forward-declare "package" instead of
> including it.
>

OK. I will try to do this tomorrow

>  I would probably do a reserve() on the random access index before
> stuffing the whole package cache into it, for efficiency.
>

OK

>
>  I think a more "aptitude"y way of handling events would be to expose
> signals (or better yet, abstract connect_ methods like you can find in
> some of my more recent code) from the package_pool corresponding
> to the signals that it emits.  It would then emit its own signals at the
> appropriate time, allowing you to handle signal ordering without adding
> a new abstraction just for handling callbacks.
>
>  I'm not really sure which of these approaches is "better" -- having an
> event-handler interface would be a bit more memory-efficient (less
> signal connections to store) and would add some uniformity to the
> code, but is different from how this is handled elsewhere in aptitude.
>
>  I also wonder how this would work with unit tests.  Ideally we would
> eventually be able to fully wrap the low-level apt cache in package_pool
> or some similar class, and then provide a fake test implementation
> that didn't require bringing up all of apt.  Using a singleton makes this
> somewhat tricky; it would also be nice if the connections weren't hidden
> inside the package_aware_object class.
>

This aware object was only a proposal, cause personally I like this
approach ;)  sigc++ signals can also be used.

To sum up, what should I do with this signals?

>  Daniel
>

-- 
Regards
Piotr Galiszewski



More information about the Aptitude-devel mailing list