
On 7/28/2011 6:12 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Thu, Jul 28, 2011 at 2:30 PM, Edward Diener<eldiener@tropicsoft.com>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@tropicsoft.com**
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