$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
Subject: Re: [boost] [type_erasure] Review started (July 18-27,2012)
From: Christophe Henry (christophe.j.henry_at_[hidden])
Date: 2012-07-26 05:57:05
Hi all,
here's my review for TypeErasure.
> Please state clearly whether you think this library should be accepted
> as a Boost library.
Let's answer this upfront: YES!
> Other questions you may want to consider:
> 1. What is your evaluation of the design?
Seen from outside, the design is clean and allows powerful constructs.
> 2. What is your evaluation of the implementation?
I only had a quick glance.
> 3. What is your evaluation of the documentation?
What is there is very good, but we could do with more documentation. A few
suggestions:
- provide a small example of usage of each concept to help new users manage
faster the learning curve.
I had some difficulties getting istreamable working and had to look at the
tests to finally get it.
- provide more real-looking examples of the sort of the polymorphic range
formatter. The more the better. I'll myself provide one later on in this
message.
> 4. What is your evaluation of the potential usefulness of the library?
Huge! And by this I mean a very interesting programming style which should
be made available also to average programmers.
Which makes the doc even more important.
> 5. Did you try to use the library? With what compiler? Did you have
> any problems?
Yes, on a real private project I do on my free time. I used VC9. I'm in
vacations so I couldn't try gcc and VC10 yet but I'll do this in the next
few weeks.
I got no problem besides a single warning about unused variable (fixed).
I tried the library for 2 different use cases:
- a streamable any (actually I need a boost-serializable any but didn't come
to it yet). One always needs something like this. In the past, I had to
modify a Boost.Any to achieve this. In my design, this helps implement my
low-level saving/loading to/from file easily as the low level layer needs no
knowledge about the types it gets, they're just a bunch of serializable
things.
- improve my MVC (Model-View-Controller) design. Here's my use case: I
started with a classical (OO style) interface-based design a graphical
editor where the user can place and edit items of different kinds on a
drawing area. The interface for these items is something along the lines:
struct IItem
{
...
virtual void createItem(...)=0; // creates in model
virtual void deleteItem(...)=0; // deletes from model
...
};
Different views dialogs however need some more concrete item types, like:
struct IType1 : public IItem
{
virtual void setName(...)=0;
virtual string const& getName() const = 0;
...
};
struct ISubType1 : public IType1
{
...
};
So far so good but then I want some classes which implement this. Where will
I implement these name members? In a class realizing IType1 (thus having to
redo it for ISubType1)? Or in a class which I also inherit from, in concrete
realizations of IType1 and ISubType1 (thus having either the dreaded diamond
or forwarding methods in all concrete classes)?
And here I have only one concept (naming) and a single type.
Get a few more of each and this will drain the life faster out of a C++
developer than a visit of a yearly vampire festival (unless you're also a
java developer, in which case you're already used to this :) ).
More seriously, the problem here is mixing interface definitions and
realizations. I changed IItem to an ItemConcept:
BOOST_TYPE_ERASURE_MEMBER((Controller)(has_createModelItem), createItem, 1);
BOOST_TYPE_ERASURE_MEMBER((Controller)(has_deleteModelItem), deleteItem, 1);
typedef ::boost::mpl::vector<
Controller::has_createItem<void(...)>,
Controller::has_deleteItem<void(...)>,
...
> ItemConcept;
typedef boost::type_erasure::any<ItemConcept> AnyItem;
I can even use composition to build my Type1Concept:
BOOST_TYPE_ERASURE_MEMBER((Controller)(has_setName), setName, 1);
BOOST_TYPE_ERASURE_MEMBER((Controller)(has_getName), getName, 0);
typedef ::boost::mpl::vector<
ItemConcept,
Controller::has_setName<void(...)>,
Controller::has_getName<...()>,
...
> Item1Concept;
typedef boost::type_erasure::any<Item1Concept> AnyItem1;
>From a user perspective, it is equivalent to use an IItem or an ItemConcept,
so that I now have a solution equivalent from the user's perspective, but
much better from the implementer's: I defined interfaces without paying any
dependency and I'm perfectly free to implement as I want, without fearing a
diamond or other ugly surprises.
While it was in front of my eyes, I realized the best part only yesterday.
Let's say my IItem would need a template method
struct IItem
{
...
// as before
virtual void createItem(...)=0; // creates in model
virtual void deleteItem(...)=0; // deletes from model
...
template <class T>
virtual void f (T& t); // will not compile
};
Sure, we all know this is not possible (sigh) . However, if I postulate that
I require T to be foo-able for all realizations of this interface, meaning I
can implement f as:
t.foo();
It is anyway good style to document the template parameter's requirements
anyway, so this bears no cost.
Ok, then I can use an any<fooable> in my concept
typedef ::boost::mpl::vector<
Controller::has_createItem<void(...)>,
Controller::has_deleteItem<void(...)>,
Controller::has_f_able<void(any<fooable>&)>,
...
> ItemConcept;
This is really close from virtual template methods.
There is only one thing missing to be able to throw away all these
interfaces: the ability to navigate through dynamic/static cast in the
hierarchy. TypeErasure supports upcasting of concepts but not downcasting.
According to Steven, this would be possible.
I won't make it an acceptance condition, but I could make good use of this
feature. My use case: different view items get an IItem, then a view Item
for Type1 would safely cast down its IItem to IType1. This is at the moment
not possible with TypeErasure. I view this as a killer feature, so I can
only advise providing it at a later point.
One last request: I would like the possibility to read the concept of an any
through a metaprogram. I found this:
/** INTERNAL ONLY */
typedef Concept _boost_type_erasure_concept_type;
I try to avoid using internals, could this be made part of the public
interface?
Use case:
My view item for Type1 gets an any<Type1Concept> and needs a property page
for it. Obviously it would need name setting/showing, which other
any<ItemConcept> would not. The property page dialog could, with a simple
template function, get the concepts supported by an any and build the dialog
accordingly. I would thus be able to write a single generic dialog class for
all types.
> 6. How much effort did you put into your evaluation? A glance? A
> quick reading? In-depth study?
About 10-15 hours trying on real, not toy code.
> 7. Are you knowledgeable about the problem domain?
I've used Boost.Any/Function several years and made my own serializable any.
I want to thank Steven for providing this great library, which I will use in
any case, accepted or not.
Thanks Lorenzo for managing the review and getting it scheduled so fast.
Christophe