|
Boost : |
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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk