Boost logo

Boost :

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




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