Boost logo

Boost :

Subject: Re: [boost] [Review] TTI
From: Jeffrey Lee Hellrung, Jr. (jeffrey.hellrung_at_[hidden])
Date: 2011-07-28 18:12:31


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.

> 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?

> 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.

  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, ...

[...]

> 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).

>
>> 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.

- Jeff


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