Subject: Re: [boost] RFC: type erasure
From: Steven Watanabe (watanabesj_at_[hidden])
Date: 2011-05-24 18:50:57


AMDG

On 05/24/2011 02:07 PM, Vicente Botet wrote:
>
> Steven Watanabe-4 wrote:
>> On 05/23/2011 02:34 PM, Vicente Botet wrote:
>>>
>>> I like how concepts are materialized using a template class that
>>> implements
>>> the default behavior of the concept and that can be specialized to map
>>> type
>>> to concepts. I understand that you need a second step to introduce the
>>> functions themselves specializing concept_interface.
>>>
>>> What I don't understand is the overloading issues. Could you use a
>>> concrete
>>> example to try to clarify the problems you have addressed?
>>>
>>
>> Okay. Problem 1: Name hiding for member functions.
>>
>> typedef mpl::vector<
>> callable&lt;int(int)&gt;,
>> callable&lt;double(double)&gt;
>>> test_concept;
>>
>> If we just use the basic definition of
>> concept_interface, the second overload
>> will hide the first since the inheritance
>> structure looks like:
>>
>> struct callable1 {
>> int operator()(int);
>> };
>>
>> struct callable2 : callable1 {
>> double operator()(double);
>> };
>>
>
> I find this normal and expected behavior, so I don't see yet why do you need
> to avoid this hiding.
>

It's normal in the sense that it's what C++
does by default. I don't see how it can possibly
be the expected behavior that:

struct func {
    template<class T>
    T operator()(T t) { return t; }
};

typedef mpl::vector<
    callable<int(int)>,
    callable<double(double)>
> test_concept;

func f1;
any<test_concept> f(f1);
f(1); // calls func::operator()<double>??

From the point of view of someone trying to
use the library, the two instances of callable
are equal. Having one hide the other is surprising.

> I don't understand the use case of the example: when a using declaration
> will be needed?
>

I can't parse this sentence.

>> For free functions, I had a problem akin to name
>> hiding when I defined a single namespace scope
>> overload with concept_interface arguments. Using
>> an inline friend function that takes the derived
>> type works with some care to avoid duplicate
>> definitions.
>>
>
> I'm sorry but I don't reach to see what is the real problem. I guess it is
> because I don't see the need of introducing the using sentence. Please could
> you present a real concrete case of overloading free functions that needs
> two different specializations of concept interface?
>

Consider operator+. Either the first argument
or the second argument or both may be a
type_erasure::any. I need two specializations,
because there are two arguments. To use
friend function defined inline I have to
inject operator+ into exactly one of the
arguments.

I originally tried to handle free functions with
namespace scope overloads like this:

template<class Base, class T, class U, class R>
typename rebind_any<Base, R>::type
operator+(
  const concept_interface<addable<T, U, R>, Base, T>&, const U&);

template<class Base, class T, class U, class R>
typename rebind_any<Base, R>::type
operator+(
  const T&, const concept_interface<addable<T, U, R>, Base, U>&);

template<class Base1, class Base2, class T, class U, class R>
typename rebind_any<Base, R>::type
operator+(
  const concept_interface<addable<T, U, R>, Base1, T>&,
  const concept_interface<addable<T, U, R>, Base2, U>&);

Unfortunately, this failed with the example
print_sequence.cpp, because of
ostreamable<_os, const char*>, ostreamable<_os, _t>
Only one of these was being considered
for overload resolution.

>>> You can check the tests for details on what I expect
>>> to work. Most of the tests for specific concepts
>>> have a test_overload test case.
>>>
>>>
> I've read some of them. I guess I understand what you expect to work, but I
> don't see why you need to specialize twice the concept_interface class for
> the same concept.

I couldn't get all the tests to pass with any other
solution I tried. If you have a simpler way to
specialize concept_interface for ostreamable or
callable that passes all my tests I'd love to
hear about it, but I've tried and this is the
best I could come up with.

To summarize the reasons for multiple specializations:
- For member functions, the second and subsequent instances
  of a concept need to be treated differently from
  the first, because they need an extra using declaration
  (which would be ill-formed in the first occurrence).
- For non-member functions, one specialization is needed
  for each argument that can be a type_erasure::any to
  make sure that the function gets added to the overload
  set regardless of what subset of the arguments is erased.

In Christ,
Steven Watanabe