Boost logo

Boost :

Subject: Re: [boost] Formal Review Request: TypeErasure
From: Daniel Larimer (dlarimer_at_[hidden])
Date: 2012-06-15 22:19:19


On Jun 15, 2012, at 10:36 AM, Hite, Christopher wrote:

> Daniel Larimer:
>> I suspect that you could achieve generic 'value semantics' for any pure virtual interface via a very simple macro + other C++11 features. I think the cost of making a call with the following code would be the same as using pointer semantics via a virtual call.
> Wow thanks for proving my point. So value<T> requires T to be copyable.
>
> Why do you use virtual inheritance? It does cost something, I think a extra word in the object which gets added to this before calling stuff on the interface.

        I have updated my example and taken it a bit beyond proto-type code. In the example below I demonstrate interface 'composition', added operator (ostream,+,etc) support and completely eliminated the need for a macro to make things work. I also demonstrated overloading operator() to create a 'boost function' type erasure. It will now store 'non class types' as it no longer inherits from the stored type so it could store non-class functions as well. With a bit more work I think I could have any< function<Signature> > working. The code no longer depends on variadic templates and should compile with VC2010.

 Virtual inheritance is needed to solve diamond inheritance problem and to support interface composition (you only want one instance to actually hold the data)

 I think that the header (any.hpp) just about covers value semantics. A slightly modified version could provide pointer and reference semantics. Pointer and reference semantics would not need to heap allocate and I believe I can avoid heap allocation for all 'small' types with a little bit of template logic.

https://github.com/bytemaster/mace/blob/master/libs/stub/include/mace/stub/any.hpp

https://github.com/bytemaster/mace/blob/master/libs/stub/include/mace/stub/any_example.cpp

        If I went back to the MACRO based approach combined with variadic templates I could eliminate one virtual indirection, but I don't know if that is enough to justify loss of portability and the use of a MACRO. I suppose there is room for both with/without a MACRO for 'fast' versions.

        With my new header I don't think I would ever use the TypeErasure library under consideration because of its three fatal flaws:

        1) Lack of readability... I could hardly follow the examples and without documentation your typical coder would never be able to follow the code or know what interface they need to expose.

        2) Lack of writability... Not only is the TypeErasure library hard to read, it is hard to figure out how to use it to do what you want.

        3) Compile times go to hell.

        Now that I have a better alternative (linked example) I think I will use it for my needs. I even think I could achieve the following:

        any<addable<int>,ostreamable,pushbackable<double>> x;

        Without drastically increasing the compile times or complexity of defining addable, ostreamable, or pushbackable.
>
>> Does this have value?? Could you integrate this technique with TypeErasure? Is it good enough for most use cases (non operator overloading)? It probably wouldn't be difficult to convert this to reference semantics and avoid heap alloc!
> I'm not sure; your non-intrusive version is so great, because the user is forced to write the delegator.
>
> I seriously doubt people that people will use any<> to replace complex interfaces. (see next post)

I have been putting a lot of thought into the various arguments against type erasure of this kind and think I have a better rational.

Why do we like templates so much? Because they allow us to program 'generically' and write algorithms that will work with 'anything' that supports some concept. Inheritance hierarchies create fragile/interdependent code. Often there is no one interface that applies perfectly to 'every use case'. If I have an interface that will work with any 'Number', why should I force my Integer class to expose a method "virtual double get()const" just so I can use it with my particular interface.

The reality is that types exist outside of where they might be used. Interfaces are 'imposed' on types and as soon as you have two or more different 'users of type' each imposing a different interface on type you get conflicts.

If you were to chart the natural relationships among types you would probably end up with a graph and not a nice hierarchy. It is the same reason why using 'tags' to group content rather than 'folders' is more natural. You have to force types into a hierarchy and often 'make compromises' that don't feel right just to 'get the inheritance right'. I have spent hours agonizing over how to resolve inheritance hierarchy conflicts.

Interfaces should be defined by the user of the object, not by the object itself. When I write a library I shouldn't force the users of my library to modify how they organize *their* objects just so that it fits how I organize *my* objects.

As a result, I think that the only reasons to use the 'traditional' interface pattern are:

1) If you want to compiler to 'enforce' interface implementation and generate compile errors for every object that needs changed when you change the interface. In this case the 'interface' owns the objects.

2) If you want to 'extend' the functionality of an existing type. This is more normal polymorphic inheritance than actual 'interfaces' though.

With my approach you still 'define' your pure virtual interfaces and so your code is still "self-documenting", you just avoid making the users of your library adopt your INTRUSIVE interface and instead your library naturally adapts to the user.

Clearly type erasure has value or we wouldn't be using boost::function, boost::any, void*, or any number of other techniques to erase types. It is also particularly useful for migrating from heavily templated code to non-templated implementations.

So perhaps we should change the nature of the discussion on this library from

"should we even have or use such a tool?"

to

"If you are going to use such a technique, what is the best library for the job?"

If there is enough interest and we can establish some more through requirements, I would gladly polish up my any<> interface to complete a simple and small library for doing this.

>
> Chris
>
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk