Boost logo

Boost :

Subject: Re: [boost] [Review] TTI
From: Edward Diener (eldiener_at_[hidden])
Date: 2011-07-29 09:26:44


On 7/28/2011 6:12 PM, Jeffrey Lee Hellrung, Jr. wrote:
> On Thu, Jul 28, 2011 at 2:30 PM, Edward Diener<eldiener_at_[hidden]>wrote:
>
>> On 7/28/2011 2:07 PM, Jeffrey Lee Hellrung, Jr. wrote:
>>
>>> On Thu, Jul 28, 2011 at 10:37 AM, Edward Diener<eldiener_at_[hidden]**
>>>> wrote:
>>>
>>> On 7/27/2011 11:49 PM, Jeffrey Lee Hellrung, Jr. wrote:
>>>>
>>> [...]
>
>> template< class T> T declval();
>>>>>
>>>>>
>>>> I think this means that T must have a default constructor ?
>>>>
>>>>
>>> Hmmm...no, but it does require T to be copyable (or movable, I
>>> guess)...however, if you want to be safe, just make T a reference type
>>> (making declval<T>() an lvalue).
>>>
>>
>> You are returning an instance of T in the signature. Does not this, for the
>> sake of the compiler, mean that T must be constructible ?
>
>
> Well, T needs to be copy-or-move-constructible (since it is being returned
> by value), unless T is a reference type. Indeed, one of the reasons to use
> such a construct is to be able to generate expressions of a type which are
> *not* default-constructible.

I understand what you are saying now.

>
>
>> If it does not mean this, then it is a clever metaprogramming trick which I
>> did not realize could be used to generate an expression at compile time
>> where one can check that an instance of T can call a member function ( your
>> declval<derived_t>().xxx(**declval<T0>()) below ). In TTI the technique
>> used has been merely to check if the address of a member function matches a
>> pointer to the correct type.
>>
>
> Keep in mind that, to me, these latter semantics (an exact signature match)
> are more in line with the rest of your library. This is why I think this
> technique *might* be more appropriately housed in Frederic's Type Traits
> Extension. But I can see arguments for why it doesn't quite fit *there*,
> either, as it (currently) deals exclusively with operators.
>
> [...]
>
>> struct base_t
>>>>> { void xxx( ) { } };
>>>>>
>>>>>
>>>> Can this be
>>>>
>>>> struct base_t
>>>> { void xxx(...) { } };
>>>>
>>>> to avoid conflict with a user's nullary function, as a variable parameter
>>>> list is far less likely than a nullary function ?
>>>>
>>>>
>>> Well it really doesn't matter, you just need base_t to have *some* member
>>> function called xxx, and I think one could argue that "void xxx ( )" is
>>> the
>>> simplest such declaration :)
>>>
>>> You actually *want* it to conflict with T::xxx.
>>>
>>
>>
>> I know what you are doing in the code. What I meant is that you do not want
>> the exact same signature for xxx in base_t as the member function of the
>> enclosing class being tested.
>
>
> Why not?

I see my error now. I had to take another glance at multiple inheritance
notation and rules in both Stroustrop and Lippman's books ( my two C++
'bibles' ) to straighten out my undersatnding.

>
>
>> In that case it is much more probable that 'void xxx(...)', as opposed to
>> 'void xxx()', does not duplicate the signature of a member function called
>> 'xxx', the former being much more rarely used in C++ than the latter.
>>
>
> I agree with this statement, but I don't think it makes any difference in
> this context.

You are correct.

>
> To elaborate a little more,
>>> base_t is only used to determine if T has a member function called xxx
>>> (signature compatibility is dealt with separately). This is effected by
>>> defining the derived_t struct
>>>
>>> struct derived_t : T, base_t { };
>>>
>>> and trying to take the address of derived_t::xxx via the call to
>>> has_mem_fn_test. If T::xxx exists, this will be ambiguous and SFINAE will
>>> kick in to select that has_mem_fn_test< derived_t>(...) overload,
>>> returning
>>> yes_type. Otherwise, if T::xxx does not exist, derived_t::xxx refers to
>>> base_t::xxx, and&base_t::xxx has type void (base_t::*)( ), so the
>>> has_mem_fn_test< derived_t>(int) overload is valid and preferred,
>>> returning
>>> no_type.
>>>
>>> Come to think of it, I'm not sure what happens when T::xxx exists but is
>>> not
>>> a member function...hmmm...I don't think I ever tested that case :(
>>>
>>
>> You mean if it is a static member function instead ?
>>
>
> ...or a type, or a template, or a member variable, ...

Right ! My brief testing shows compiler errors because has_mem_fn< T,
void >::value is true and therefore the constructs in has_mem_fn_helper
are invalid C++.

>
> [...]
>
>> Yeah, that's among the various things I left
>>> out. Also const correctness and lvalue/rvalue preservation need to be
>>> added. And all that is much easier than addressing void result types,
>>> which
>>> requires another round of indirection :/
>>>
>>
>> I believe Frederick Bron had to tackle the latter and I remember
>> discussions about dealing with it by Eric Niebler among others.
>>
>
> Right.
>
> [...]
>
>> Maybe a generic method for any number of types would be possible through
>>>
>>>> using Boost function types.
>>>>
>>>>
>>> The way to go, I think, is BOOST_PP_ITERATE (since you have to construct
>>> the
>>> actual call expression declval<T>().xxx(declval<T0>()**, declval<T1>(),
>>> ...,
>>> declval<T[N-1]>()))...which implies that you'll have to have a
>>>
>>> #define BOOST_TTI_PARAM_1 foo
>>> #define BOOST_TTI_PARAM_2 bar
>>> #include BOOST_TTI_HAS_MEMBER_FUNCTION_**THINGY()
>>>
>>> interface rather than a metafunction-generating macro interface. I guess
>>> you can use BOOST_PP_REPEAT but it could make the implementation unwieldy
>>> and make it potentially very difficult to track down usage errors :/
>>> Variadic templates *might* work here, but I'm really not sure.
>>>
>>
>> I understand in general how to use pp-lib to generate the code for some
>> maximum number of parameters. As much as I admire pp-lib, if there is a
>> non-macro using solution to most anything, I would rather use that.
>>
>
> Then I *think* you're "stuck" with variadic templates...which obviously
> limits the applicability to recent versions of gcc and clang (and, I don't
> know, maybe Intel? certainly not MSVC yet).

Not necessarily. In TTI I currently use Boost function types to
synthesize a notation of a member function with a return type and an
unlimited number of parameters into what I need, and I may be able to do
a similar thing with your code. Of course I am not saying I can't use
macros also when I need it.

>
>
>>
>>> I will definitely add this to TTI in some form or other. Thanks for the
>>>> code !
>>>>
>>>>
>>> You may want to coordinate with Frederic Bron and his Type Traits
>>> Extension,
>>> as this functionality is kind of an overlap between his and your
>>> libraries.
>>>
>>
>> I will study what Frederick has done and try to use what you have above and
>> what he has done for TTI. If there is some overlap I will not tread on his
>> territory unless the functionality I may try to provide is essentially
>> undoable by what he has already done. It is also possible that common
>> metafunctions, which are details to both implementations, between the
>> libraries can be shared.
>>
>
> Like I said, I'm *pretty sure* that Frederic doesn't have the facilities to
> construct such a query; I was only commenting that this construct lies at
> the intersection of the domains of both of your libraries. I mean, both of
> your libraries deal with syntactical queries on types, and each just has a
> different focus.

I need to look at Frederick's implementation anyway just to understand
how he is doing what he is doing in case I can use some of his
techniques in future updates to TTI.

Eddie


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