
What is your evaluation of the design? Except for the namespace issue, which seems resolved, it seems to be what it needs to be. I don't know that there are a lot of design options here. It seems to offer a complete set of needs, from metafunction or trait to metafunction class. This is good. What is your evaluation of the implementation? Again, I don't see a lot of room to be innovative here. It's as clean as it can be I think. One thing that I think is missing is the option to check for a named callable within a type. For example, one might want to use enable_if style concept checking. So long as a type has function 'xxx' that takes arguments of type x,y,and z, and returns something convertible to type A, the concept is obeyed. I don't believe this can be done with C++03, but with C++1X I was able to do something using decltype. What is your evaluation of the documentation? Could probably use some more tutorials. What is your evaluation of the potential usefulness of the library? VERY. Having to write all these things by hand is a PITA. Most people who would have use for this library have probably already devised similar sets of macros. It would be nice to have something somewhat standardized. Did you try to use the library? No. How much effort did you put into your evaluation? Quick reading. Are you knowledgeable about the problem domain? I think I can say yes. I've had to write my own such SFINAE and overload metafunction objects numerous times. Do you think the library should be accepted as a Boost library? Yes, so long as the namespace issue is indeed fixed. For thoroughness, the namespace issue is that the current implementation places traits and metafunction objects within the boost namespace. The library would be more useful if the macro declared objects within the scope the macro is used. It's an important feature that I often declare such objects within class scope. Since they aren't providing specializations, it shouldn't be necessary to place them in the boost namespace and the author has agreed to discontinue this policy.

On Tue, Jul 26, 2011 at 1:19 PM, Noah Roberts <roberts.noah@gmail.com>wrote: [...]
One thing that I think is missing is the option to check for a named callable within a type. For example, one might want to use enable_if style concept checking. So long as a type has function 'xxx' that takes arguments of type x,y,and z, and returns something convertible to type A, the concept is obeyed. I don't believe this can be done with C++03, but with C++1X I was able to do something using decltype.
[...] Actually, you can approximate it pretty well in C++03 (using derivation and using tricks) to the point that I think it will work pretty much as you expect except for nullary member functions, where your queries must be more restrictive. This is a useful query, but I believe Edward has decided it to be outside the scope of TTI. - Jeff

On 7/26/2011 5:03 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Tue, Jul 26, 2011 at 1:19 PM, Noah Roberts<roberts.noah@gmail.com>wrote: [...]
One thing that I think is missing is the option to check for a named callable within a type. For example, one might want to use enable_if style concept checking. So long as a type has function 'xxx' that takes arguments of type x,y,and z, and returns something convertible to type A, the concept is obeyed. I don't believe this can be done with C++03, but with C++1X I was able to do something using decltype.
[...]
Actually, you can approximate it pretty well in C++03 (using derivation and using tricks) to the point that I think it will work pretty much as you expect except for nullary member functions, where your queries must be more restrictive. This is a useful query, but I believe Edward has decided it to be outside the scope of TTI.
I am not sure what the OP means by 'named callable". If it can be introspected within the type I can consider whether it can be done or not. Eddie

On Tue, Jul 26, 2011 at 2:25 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/26/2011 5:03 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Tue, Jul 26, 2011 at 1:19 PM, Noah Roberts<roberts.noah@gmail.com**
wrote: [...]
One thing that I think is missing is the option to check for a named
callable within a type. For example, one might want to use enable_if style concept checking. So long as a type has function 'xxx' that takes arguments of type x,y,and z, and returns something convertible to type A, the concept is obeyed. I don't believe this can be done with C++03, but with C++1X I was able to do something using decltype.
[...]
Actually, you can approximate it pretty well in C++03 (using derivation and using tricks) to the point that I think it will work pretty much as you expect except for nullary member functions, where your queries must be more restrictive. This is a useful query, but I believe Edward has decided it to be outside the scope of TTI.
I am not sure what the OP means by 'named callable". If it can be introspected within the type I can consider whether it can be done or not.
I think you missed the convertibility requirement(s). This is something more along the lines of Frederic Bron's Type Traits Extension, I think. - Jeff

On 7/26/2011 5:58 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Tue, Jul 26, 2011 at 2:25 PM, Edward Diener<eldiener@tropicsoft.com>wrote:
On 7/26/2011 5:03 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Tue, Jul 26, 2011 at 1:19 PM, Noah Roberts<roberts.noah@gmail.com**
wrote: [...]
One thing that I think is missing is the option to check for a named
callable within a type. For example, one might want to use enable_if style concept checking. So long as a type has function 'xxx' that takes arguments of type x,y,and z, and returns something convertible to type A, the concept is obeyed. I don't believe this can be done with C++03, but with C++1X I was able to do something using decltype.
[...]
Actually, you can approximate it pretty well in C++03 (using derivation and using tricks) to the point that I think it will work pretty much as you expect except for nullary member functions, where your queries must be more restrictive. This is a useful query, but I believe Edward has decided it to be outside the scope of TTI.
I am not sure what the OP means by 'named callable". If it can be introspected within the type I can consider whether it can be done or not.
I think you missed the convertibility requirement(s).
TTI needs an exact signature.
This is something more along the lines of Frederic Bron's Type Traits Extension, I think.
That deals with operators for a type in general. I have a feeling that the OP was interested in determining whether a class was a function object for some parameter(s).

On Wed, Jul 27, 2011 at 7:16 AM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/26/2011 5:58 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Tue, Jul 26, 2011 at 2:25 PM, Edward Diener<eldiener@tropicsoft.com**
wrote:
On 7/26/2011 5:03 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Tue, Jul 26, 2011 at 1:19 PM, Noah Roberts<roberts.noah@gmail.com**
**
wrote:
[...]
One thing that I think is missing is the option to check for a named
callable within a type. For example, one might want to use enable_if style concept checking. So long as a type has function 'xxx' that takes arguments of type x,y,and z, and returns something convertible to type A, the concept is obeyed. I don't believe this can be done with C++03, but with C++1X I was able to do something using decltype.
[...]
Actually, you can approximate it pretty well in C++03 (using derivation and using tricks) to the point that I think it will work pretty much as you expect except for nullary member functions, where your queries must be more restrictive. This is a useful query, but I believe Edward has decided it to be outside the scope of TTI.
I am not sure what the OP means by 'named callable". If it can be introspected within the type I can consider whether it can be done or not.
I think you missed the convertibility requirement(s).
TTI needs an exact signature.
...and hence why I had presumed it to be outside of the scope of TTI. It stills falls within the domain of introspection, though.
This is something more along the lines of Frederic Bron's Type Traits
Extension, I think.
That deals with operators for a type in general. I have a feeling that the OP was interested in determining whether a class was a function object for some parameter(s).
Frederic's framework is not really specific to operators, per se. For example, I believe his techniques could be used to detect whether a free function would be found via ADL. The proposed Type Traits Extension is all about "can you call operator X with arguments Y with result convertible to Z?". It's not such a large leap to ask the same question about a member function. - Jeff

On 07/27/2011 07:52 PM, Jeffrey Lee Hellrung, Jr. wrote:
I think you missed the convertibility requirement(s).
TTI needs an exact signature.
...and hence why I had presumed it to be outside of the scope of TTI. It stills falls within the domain of introspection, though.
You need SFINAE for expressions to do this.

On Wed, Jul 27, 2011 at 12:23 PM, Mathias Gaunard < mathias.gaunard@ens-lyon.org> wrote:
On 07/27/2011 07:52 PM, Jeffrey Lee Hellrung, Jr. wrote:
I think you missed the convertibility requirement(s).
TTI needs an exact signature.
...and hence why I had presumed it to be outside of the scope of TTI. It stills falls within the domain of introspection, though.
You need SFINAE for expressions to do this.
You can get a pretty good approximation via something like the following (warning: untested). -------- struct yes_type { char _dummy[1]; }; struct no_type { char _dummy[2]; }; struct dummy_result_t { }; yes_type is_dummy_result(dummy_result_t); no_type is_dummy_result(...); struct base_t { void xxx() { } }; template< void (base_t::*)( ) > struct has_mem_fn_detector { typedef no_type type; }; template< class T > struct derived_t : T, base_t { using T::xxx; dummy_result_t xxx(...) const; }; template< class T > typename has_mem_fn_detector< &T::xxx >::type has_mem_fn_test(int); template< class T > yes_type has_mem_fn_test(...); -------- First, you determine if T *has* a member function called xxx, regardless of signature, via struct derived2_t : T, base_t { }; sizeof( has_mem_fn_test< derived2_t >(0) ) == sizeof( yes_type ) and, if this passes, then you use the expression declval< derived_t<T>
().xxx(arg0, arg1) (for example) to query, first, whether the type of the expression is dummy_result_t; then, assuming not, whether the type of the expression has the convertibility properties you desire (for example).
It's not perfect, e.g., it breaks when xxx is private or if you want to check nullary member functions (must resort to exact signatures in that case, I think), and I haven't tested what happens when xxx is a static member function. But I don't think any of this is using SFINAE for expressions. - Jeff

On 7/27/2011 4:14 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Wed, Jul 27, 2011 at 12:23 PM, Mathias Gaunard< mathias.gaunard@ens-lyon.org> wrote:
On 07/27/2011 07:52 PM, Jeffrey Lee Hellrung, Jr. wrote:
I think you missed the convertibility requirement(s).
TTI needs an exact signature.
...and hence why I had presumed it to be outside of the scope of TTI. It stills falls within the domain of introspection, though.
You need SFINAE for expressions to do this.
You can get a pretty good approximation via something like the following (warning: untested).
-------- struct yes_type { char _dummy[1]; }; struct no_type { char _dummy[2]; };
struct dummy_result_t { }; yes_type is_dummy_result(dummy_result_t); no_type is_dummy_result(...);
struct base_t { void xxx() { } }; template< void (base_t::*)( )> struct has_mem_fn_detector { typedef no_type type; };
template< class T> struct derived_t : T, base_t { using T::xxx; dummy_result_t xxx(...) const; };
template< class T> typename has_mem_fn_detector< &T::xxx>::type has_mem_fn_test(int); template< class T> yes_type has_mem_fn_test(...); --------
First, you determine if T *has* a member function called xxx, regardless of signature, via
struct derived2_t : T, base_t { }; sizeof( has_mem_fn_test< derived2_t>(0) ) == sizeof( yes_type )
Something is missing here. You have derived2_t as a struct but clearly you mean it as a template class since there is no base class T. But if it is a template class it can not be passed as a type to has_mem_fn_test. I am interested in what you may be trying to do here, so that is why I am pointing this out. Can you correct and explain what you are doing again ? I would like to understand the technique for checking convertibility. If what you are doing is similar to what Frederick Bron has for type traits operators, I will check his code instead.
and, if this passes, then you use the expression declval< derived_t<T>
().xxx(arg0, arg1) (for example) to query, first, whether the type of the expression is dummy_result_t; then, assuming not, whether the type of the expression has the convertibility properties you desire (for example).
It's not perfect, e.g., it breaks when xxx is private or if you want to check nullary member functions (must resort to exact signatures in that case, I think), and I haven't tested what happens when xxx is a static member function. But I don't think any of this is using SFINAE for expressions.

On Wed, Jul 27, 2011 at 4:23 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 7/27/2011 4:14 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Wed, Jul 27, 2011 at 12:23 PM, Mathias Gaunard< mathias.gaunard@ens-lyon.org> wrote:
On 07/27/2011 07:52 PM, Jeffrey Lee Hellrung, Jr. wrote:
I think you missed the convertibility requirement(s).
TTI needs an exact signature.
...and hence why I had presumed it to be outside of the scope of TTI. It stills falls within the domain of introspection, though.
You need SFINAE for expressions to do this.
You can get a pretty good approximation via something like the following (warning: untested).
-------- struct yes_type { char _dummy[1]; }; struct no_type { char _dummy[2]; };
struct dummy_result_t { }; yes_type is_dummy_result(dummy_result_**t); no_type is_dummy_result(...);
struct base_t { void xxx() { } }; template< void (base_t::*)( )> struct has_mem_fn_detector { typedef no_type type; };
template< class T> struct derived_t : T, base_t { using T::xxx; dummy_result_t xxx(...) const; };
template< class T> typename has_mem_fn_detector< &T::xxx>::type has_mem_fn_test(int); template< class T> yes_type has_mem_fn_test(...); --------
First, you determine if T *has* a member function called xxx, regardless of signature, via
struct derived2_t : T, base_t { }; sizeof( has_mem_fn_test< derived2_t>(0) ) == sizeof( yes_type )
Something is missing here. You have derived2_t as a struct but clearly you mean it as a template class since there is no base class T. But if it is a template class it can not be passed as a type to has_mem_fn_test.
No, I meant it as a plain struct; I'm assuming T is already defined in the enclosing context, i.e., something like template< class T > has_member_function_xxx { struct derived2_t : T, base_t { }; ... check if T::xxx exists via has_mem_fn_test... ... if so, check if T.xxx has a compatible signature ... }; I am interested in what you may be trying to do here, so that is why I am
pointing this out.
Can you correct and explain what you are doing again ? I would like to understand the technique for checking convertibility.
Let me get a full example put together, then.
If what you are doing is similar to what Frederick Bron has for type traits operators, I will check his code instead.
It is similar in spirit, but more of a PITA. BTW, these aren't entirely my original ideas, but I forget where I pilfered them from :/
and, if this passes, then you use the expression declval< derived_t<T>
().xxx(arg0, arg1) (for example) to query, first, whether the type of the
expression is dummy_result_t; then, assuming not, whether the type of the expression has the convertibility properties you desire (for example).
It's not perfect, e.g., it breaks when xxx is private or if you want to check nullary member functions (must resort to exact signatures in that case, I think), and I haven't tested what happens when xxx is a static member function. But I don't think any of this is using SFINAE for expressions.
- Jeff

On Wed, Jul 27, 2011 at 4:23 PM, Edward Diener <eldiener@tropicsoft.com>wrote: [...]
I am interested in what you may be trying to do here, so that is why I am pointing this out.
Can you correct and explain what you are doing again ? I would like to understand the technique for checking convertibility.
[...] The following program should illustrate the basic idea. Obviously, this is pretty minimalistic, so it doesn't take into account constness, only checks unary member functions, doesn't account for void result types, etc. Tested on MSVC9. -------- #include <boost/mpl/and.hpp> #include <boost/static_assert.hpp> template< class T > T declval(); struct yes_type { char _dummy[1]; }; struct no_type { char _dummy[2]; }; template< class T > struct is_convertible { static yes_type apply(T); static no_type apply(...); }; struct dummy_result_t { }; yes_type is_dummy_result_t(dummy_result_t); no_type is_dummy_result_t(...); struct base_t { void xxx( ) { } }; template< void (base_t::*)( ) > struct has_mem_fn_detector { typedef no_type type; }; template< class T > typename has_mem_fn_detector< &T::xxx >::type has_mem_fn_test(int); template< class T > yes_type has_mem_fn_test(...); template< class T > struct derived_t : T { using T::xxx; dummy_result_t xxx(...) const; }; template< class T, class Signature > struct has_mem_fn_helper; template< class T, class Signature = void > struct has_mem_fn : boost::mpl::and_< has_mem_fn< T, void >, has_mem_fn_helper< T, Signature > > { }; template< class T > struct has_mem_fn< T, void > { struct derived_t : T, base_t { }; static const bool value = sizeof( has_mem_fn_test< derived_t >(0) ) == sizeof( yes_type ); typedef has_mem_fn type; }; template< class T, class Result, class T0 > struct has_mem_fn_helper< T, Result ( T0 ) > { static const bool value = (sizeof( is_dummy_result_t(declval< derived_t<T>
().xxx(declval<T0>())) ) == sizeof( no_type )) && (sizeof( is_convertible< Result >::apply(declval< derived_t<T> ().xxx(declval<T0>())) ) == sizeof( yes_type )); typedef has_mem_fn_helper type; };
struct X { }; struct Y { int xxx(int); }; int main(int argc, char* argv[]) { BOOST_STATIC_ASSERT(!(has_mem_fn< X >::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y >::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y, int ( int ) >::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y, long ( short ) >::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Y, int ( void* ) >::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Y, void* ( int ) >::value)); return 0; } -------- - Jeff

On 7/27/2011 11:49 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Wed, Jul 27, 2011 at 4:23 PM, Edward Diener<eldiener@tropicsoft.com>wrote: [...]
I am interested in what you may be trying to do here, so that is why I am pointing this out.
Can you correct and explain what you are doing again ? I would like to understand the technique for checking convertibility.
[...]
The following program should illustrate the basic idea. Obviously, this is pretty minimalistic, so it doesn't take into account constness, only checks unary member functions, doesn't account for void result types, etc.
Tested on MSVC9.
--------
#include<boost/mpl/and.hpp> #include<boost/static_assert.hpp>
template< class T> T declval();
I think this means that T must have a default constructor ? If so, it is limiting for the enclosing type and the parameter types of the function signature.
struct yes_type { char _dummy[1]; }; struct no_type { char _dummy[2]; };
template< class T> struct is_convertible { static yes_type apply(T); static no_type apply(...); };
struct dummy_result_t { }; yes_type is_dummy_result_t(dummy_result_t); no_type is_dummy_result_t(...);
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 ?
template< void (base_t::*)( )> struct has_mem_fn_detector { typedef no_type type; };
template< class T> typename has_mem_fn_detector< &T::xxx>::type has_mem_fn_test(int);
template< class T> yes_type has_mem_fn_test(...);
template< class T> struct derived_t : T { using T::xxx; dummy_result_t xxx(...) const; };
template< class T, class Signature> struct has_mem_fn_helper;
template< class T, class Signature = void> struct has_mem_fn : boost::mpl::and_< has_mem_fn< T, void>, has_mem_fn_helper< T, Signature> > { };
template< class T> struct has_mem_fn< T, void> { struct derived_t : T, base_t { }; static const bool value = sizeof( has_mem_fn_test< derived_t>(0) ) == sizeof( yes_type ); typedef has_mem_fn type; };
template< class T, class Result, class T0> struct has_mem_fn_helper< T, Result ( T0 )> { static const bool value = (sizeof( is_dummy_result_t(declval< derived_t<T>
().xxx(declval<T0>())) ) == sizeof( no_type )) && (sizeof( is_convertible< Result>::apply(declval< derived_t<T> ().xxx(declval<T0>())) ) == sizeof( yes_type )); typedef has_mem_fn_helper type; };
struct X { };
struct Y { int xxx(int); };
int main(int argc, char* argv[]) { BOOST_STATIC_ASSERT(!(has_mem_fn< X>::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y>::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y, int ( int )>::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y, long ( short )>::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Y, int ( void* )>::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Y, void* ( int )>::value)); return 0; }
Very neat. Of course T0 would have to be expanded to more possible types for function parameter matching. Maybe a generic method for any number of types would be possible through using Boost function types. I will definitely add this to TTI in some form or other. Thanks for the code ! Eddie

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:
On Wed, Jul 27, 2011 at 4:23 PM, Edward Diener<eldiener@tropicsoft.com**
wrote: [...]
I am interested in what you may be trying to do here, so that is why I am
pointing this out.
Can you correct and explain what you are doing again ? I would like to understand the technique for checking convertibility.
[...]
The following program should illustrate the basic idea. Obviously, this is pretty minimalistic, so it doesn't take into account constness, only checks unary member functions, doesn't account for void result types, etc.
Tested on MSVC9.
--------
#include<boost/mpl/and.hpp> #include<boost/static_assert.**hpp>
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).
If so, it is limiting for the enclosing type and the parameter types of the function signature.
The idea is to take any reference qualifiers (or lack thereof) on the enclosing type and parameter types literally, to preserve rvalue/lvalue-ness. That's straightforward to do for the parameter types (nothing special need be done), but I did *not* take such care for the enclosing type (notice the lack of boost::remove_reference's below), just for simplicity of exposition.
struct yes_type { char _dummy[1]; };
struct no_type { char _dummy[2]; };
template< class T> struct is_convertible { static yes_type apply(T); static no_type apply(...); };
struct dummy_result_t { }; yes_type is_dummy_result_t(dummy_**result_t); no_type is_dummy_result_t(...);
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. 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 :( template< void (base_t::*)( )>
struct has_mem_fn_detector { typedef no_type type; };
template< class T> typename has_mem_fn_detector< &T::xxx>::type has_mem_fn_test(int);
template< class T> yes_type has_mem_fn_test(...);
template< class T> struct derived_t : T { using T::xxx; dummy_result_t xxx(...) const; };
template< class T, class Signature> struct has_mem_fn_helper;
template< class T, class Signature = void> struct has_mem_fn : boost::mpl::and_< has_mem_fn< T, void>, has_mem_fn_helper< T, Signature> > { };
template< class T> struct has_mem_fn< T, void> { struct derived_t : T, base_t { }; static const bool value = sizeof( has_mem_fn_test< derived_t>(0) ) == sizeof( yes_type ); typedef has_mem_fn type; };
template< class T, class Result, class T0> struct has_mem_fn_helper< T, Result ( T0 )> { static const bool value = (sizeof( is_dummy_result_t(declval< derived_t<T>
().xxx(declval<T0>())) ) == sizeof( no_type ))
&& (sizeof( is_convertible< Result>::apply(declval< derived_t<T>
().xxx(declval<T0>())) ) == sizeof( yes_type ));
typedef has_mem_fn_helper type; };
struct X { };
struct Y { int xxx(int); };
int main(int argc, char* argv[]) { BOOST_STATIC_ASSERT(!(has_mem_**fn< X>::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y>::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y, int ( int )>::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y, long ( short )>::value)); BOOST_STATIC_ASSERT(!(has_mem_**fn< Y, int ( void* )>::value)); BOOST_STATIC_ASSERT(!(has_mem_**fn< Y, void* ( int )>::value)); return 0; }
Very neat. Of course T0 would have to be expanded to more possible types for function parameter matching.
You mean other arities than 1? 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 :/ This probably wouldn't surprise you given my comments on TTI, but I've also found it useful to include a 3rd template parameter, which is an MPL lambda expression to be applied to the result type of the xxx member function (to enable queries other than convertibility). 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 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. - Jeff

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:
On Wed, Jul 27, 2011 at 4:23 PM, Edward Diener<eldiener@tropicsoft.com**
wrote: [...]
I am interested in what you may be trying to do here, so that is why I am
pointing this out.
Can you correct and explain what you are doing again ? I would like to understand the technique for checking convertibility.
[...]
The following program should illustrate the basic idea. Obviously, this is pretty minimalistic, so it doesn't take into account constness, only checks unary member functions, doesn't account for void result types, etc.
Tested on MSVC9.
--------
#include<boost/mpl/and.hpp> #include<boost/static_assert.**hpp>
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 ? 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.
If so, it is limiting for the enclosing type and the parameter types of the function signature.
The idea is to take any reference qualifiers (or lack thereof) on the enclosing type and parameter types literally, to preserve rvalue/lvalue-ness. That's straightforward to do for the parameter types (nothing special need be done), but I did *not* take such care for the enclosing type (notice the lack of boost::remove_reference's below), just for simplicity of exposition.
struct yes_type { char _dummy[1]; };
struct no_type { char _dummy[2]; };
template< class T> struct is_convertible { static yes_type apply(T); static no_type apply(...); };
struct dummy_result_t { }; yes_type is_dummy_result_t(dummy_**result_t); no_type is_dummy_result_t(...);
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. 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.
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 ?
template< void (base_t::*)( )>
struct has_mem_fn_detector { typedef no_type type; };
template< class T> typename has_mem_fn_detector< &T::xxx>::type has_mem_fn_test(int);
template< class T> yes_type has_mem_fn_test(...);
template< class T> struct derived_t : T { using T::xxx; dummy_result_t xxx(...) const; };
template< class T, class Signature> struct has_mem_fn_helper;
template< class T, class Signature = void> struct has_mem_fn : boost::mpl::and_< has_mem_fn< T, void>, has_mem_fn_helper< T, Signature> > { };
template< class T> struct has_mem_fn< T, void> { struct derived_t : T, base_t { }; static const bool value = sizeof( has_mem_fn_test< derived_t>(0) ) == sizeof( yes_type ); typedef has_mem_fn type; };
template< class T, class Result, class T0> struct has_mem_fn_helper< T, Result ( T0 )> { static const bool value = (sizeof( is_dummy_result_t(declval< derived_t<T>
().xxx(declval<T0>())) ) == sizeof( no_type ))
&& (sizeof( is_convertible< Result>::apply(declval< derived_t<T>
().xxx(declval<T0>())) ) == sizeof( yes_type ));
typedef has_mem_fn_helper type; };
struct X { };
struct Y { int xxx(int); };
int main(int argc, char* argv[]) { BOOST_STATIC_ASSERT(!(has_mem_**fn< X>::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y>::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y, int ( int )>::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y, long ( short )>::value)); BOOST_STATIC_ASSERT(!(has_mem_**fn< Y, int ( void* )>::value)); BOOST_STATIC_ASSERT(!(has_mem_**fn< Y, void* ( int )>::value)); return 0; }
Very neat. Of course T0 would have to be expanded to more possible types for function parameter matching.
You mean other arities than 1?
Bingo !
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.
This probably wouldn't surprise you given my comments on TTI, but I've also found it useful to include a 3rd template parameter, which is an MPL lambda expression to be applied to the result type of the xxx member function (to enable queries other than convertibility).
No it does not surprise me but first things first.
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.
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. Eddie

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

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

On Fri, Jul 29, 2011 at 6:26 AM, Edward Diener <eldiener@tropicsoft.com>wrote: [...]
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.
The (big) difference here is that you need to construct a member function call *expression* (which necessarily requires the name of the member function), not a member function *type*...indeed, you can't even know the signature of the member function you want to call, since you're relying on the compiler to determine the right one based on overload resolution. - Jeff

On 28/07/11 19:07, Jeffrey Lee Hellrung, Jr. wrote: <snip>
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.
Yes, variadic templates make it easy; e.g. this alteration to the end of your code works for me with clang++: template< class T, class Result, class... Args > struct has_mem_fn_helper< T, Result ( Args... ) > { static const bool value = (sizeof( is_dummy_result_t( declval< derived_t<T>>().xxx(declval<Args>()...)) ) == sizeof( no_type )) && (sizeof( is_convertible< Result >::apply( declval< derived_t<T>>().xxx(declval<Args>()...)) ) == sizeof( yes_type )); typedef has_mem_fn_helper type; }; struct X { }; struct Y { int xxx(int); }; struct Z { int xxx(int, int); }; int main(int /*argc*/, char* /*argv*/[]) { BOOST_STATIC_ASSERT(!(has_mem_fn< X >::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y >::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y, int ( int ) >::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y, long ( short ) >::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Y, int ( void* ) >::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Y, void* ( int ) >::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Z, int ( int ) >::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Z, int ( void* ) >::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Z, int ( int, long ) >::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Z, int ( void*, int ) >::value)); return 0; } John Bytheway

On Thu, Jul 28, 2011 at 2:49 PM, John Bytheway <jbytheway+boost@gmail.com>wrote:
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
On 28/07/11 19:07, Jeffrey Lee Hellrung, Jr. wrote: <snip> 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.
Yes, variadic templates make it easy; e.g. this alteration to the end of your code works for me with clang++:
template< class T, class Result, class... Args > struct has_mem_fn_helper< T, Result ( Args... ) > { static const bool value = (sizeof( is_dummy_result_t( declval< derived_t<T>>().xxx(declval<Args>()...)) ) == sizeof( no_type )) && (sizeof( is_convertible< Result >::apply( declval< derived_t<T>>().xxx(declval<Args>()...)) ) == sizeof( yes_type )); typedef has_mem_fn_helper type; };
struct X { };
struct Y { int xxx(int); };
struct Z { int xxx(int, int); };
int main(int /*argc*/, char* /*argv*/[]) { BOOST_STATIC_ASSERT(!(has_mem_fn< X >::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y >::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y, int ( int ) >::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Y, long ( short ) >::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Y, int ( void* ) >::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Y, void* ( int ) >::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Z, int ( int ) >::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Z, int ( void* ) >::value)); BOOST_STATIC_ASSERT( (has_mem_fn< Z, int ( int, long ) >::value)); BOOST_STATIC_ASSERT(!(has_mem_fn< Z, int ( void*, int ) >::value)); return 0; }
Awesome! Unfortunately, not supported in Visual Studio yet :( - Jeff

On 7/26/2011 2:25 PM, Edward Diener wrote:
On 7/26/2011 5:03 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Tue, Jul 26, 2011 at 1:19 PM, Noah Roberts<roberts.noah@gmail.com>wrote: [...]
One thing that I think is missing is the option to check for a named callable within a type. For example, one might want to use enable_if style concept checking. So long as a type has function 'xxx' that takes arguments of type x,y,and z, and returns something convertible to type A, the concept is obeyed. I don't believe this can be done with C++03, but with C++1X I was able to do something using decltype.
[...]
Actually, you can approximate it pretty well in C++03 (using derivation and using tricks) to the point that I think it will work pretty much as you expect except for nullary member functions, where your queries must be more restrictive. This is a useful query, but I believe Edward has decided it to be outside the scope of TTI.
I am not sure what the OP means by 'named callable". If it can be introspected within the type I can consider whether it can be done or not.
What I mean by "callable" is that the expression may not be just a function call but could also be a template instantiation. For example: Concept C z = c.fun(x) | x is an int | result convertible to double This class obeys: struct class_a { double fun(int); }; As does this one: struct class_b { template < typename T > double fun(T); }; As does this one: struct class_c { template < typename T > converts_to_double_type fun(T); }; And this: struct class_d { double fun(has_implicit_int_constructor); }; etc... The question being asked here is, can I call something called X within type T that accepts parameters x,y,z and returns something I can assign to type A? Answering this question is often more interesting than the question, "Does type T have function X with signature Y?" It also cannot be correctly answered with such a check because it could be implemented by template, a function that has default arguments, or a function that takes other types but they are convertible. I would consider this within the scope of "introspection", but I can see why it might be omitted for a future version or something.

On 7/27/2011 12:51 PM, Noah Roberts wrote:
On 7/26/2011 2:25 PM, Edward Diener wrote:
On 7/26/2011 5:03 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Tue, Jul 26, 2011 at 1:19 PM, Noah Roberts<roberts.noah@gmail.com>wrote: [...]
One thing that I think is missing is the option to check for a named callable within a type. For example, one might want to use enable_if style concept checking. So long as a type has function 'xxx' that takes arguments of type x,y,and z, and returns something convertible to type A, the concept is obeyed. I don't believe this can be done with C++03, but with C++1X I was able to do something using decltype.
[...]
Actually, you can approximate it pretty well in C++03 (using derivation and using tricks) to the point that I think it will work pretty much as you expect except for nullary member functions, where your queries must be more restrictive. This is a useful query, but I believe Edward has decided it to be outside the scope of TTI.
I am not sure what the OP means by 'named callable". If it can be introspected within the type I can consider whether it can be done or not.
What I mean by "callable" is that the expression may not be just a function call but could also be a template instantiation. For example:
Concept C z = c.fun(x) | x is an int | result convertible to double
This class obeys:
struct class_a { double fun(int); };
This can be determined by TTI.
As does this one:
struct class_b { template < typename T > double fun(T); };
This is actually doable in TTI but I dropped the implementation because of compiler bugs in gcc and vc++ ( clang works ). See the section in the documentation called "Introspecting Function Templates".
As does this one:
struct class_c { template < typename T > converts_to_double_type fun(T); };
See above. My experimental implementation in TTI might actually work with a converted return value, but offhand I do not think so.
And this:
struct class_d { double fun(has_implicit_int_constructor); };
Jeffrey Hellrung has shown me some code and techniques I will adapt for TTI which could do this.
etc...
The question being asked here is, can I call something called X within type T that accepts parameters x,y,z and returns something I can assign to type A?
Answering this question is often more interesting than the question, "Does type T have function X with signature Y?" It also cannot be correctly answered with such a check because it could be implemented by template, a function that has default arguments, or a function that takes other types but they are convertible.
It does seem reasonable to ask that question. I will work on the idea you mention of being able to specify any callable in TTI.
I would consider this within the scope of "introspection", but I can see why it might be omitted for a future version or something.
Eddie

On 7/26/2011 4:19 PM, Noah Roberts wrote:
What is your evaluation of the design?
Except for the namespace issue, which seems resolved, it seems to be what it needs to be. I don't know that there are a lot of design options here. It seems to offer a complete set of needs, from metafunction or trait to metafunction class. This is good.
What is your evaluation of the implementation?
Again, I don't see a lot of room to be innovative here. It's as clean as it can be I think.
One thing that I think is missing is the option to check for a named callable within a type. For example, one might want to use enable_if style concept checking. So long as a type has function 'xxx' that takes arguments of type x,y,and z, and returns something convertible to type A, the concept is obeyed. I don't believe this can be done with C++03, but with C++1X I was able to do something using decltype.
By "named callable" do you mean a member function or static member function of the type ? TTI can check for that. Or is there some other criteria for which you are looking ?
What is your evaluation of the documentation?
Could probably use some more tutorials.
This is a good suggestion.
What is your evaluation of the potential usefulness of the library?
VERY. Having to write all these things by hand is a PITA. Most people who would have use for this library have probably already devised similar sets of macros. It would be nice to have something somewhat standardized.
Did you try to use the library?
No.
How much effort did you put into your evaluation?
Quick reading.
Are you knowledgeable about the problem domain?
I think I can say yes. I've had to write my own such SFINAE and overload metafunction objects numerous times.
Do you think the library should be accepted as a Boost library?
Yes, so long as the namespace issue is indeed fixed.
Appreciated.
For thoroughness, the namespace issue is that the current implementation places traits and metafunction objects within the boost namespace. The library would be more useful if the macro declared objects within the scope the macro is used. It's an important feature that I often declare such objects within class scope. Since they aren't providing specializations, it shouldn't be necessary to place them in the boost namespace and the author has agreed to discontinue this policy.
I will definitely be removing the metafunctions generated by the macros from any namespace. Thanks for your review ! Eddie Diener

One thing that I think is missing is the option to check for a named callable within a type. For example, one might want to use enable_if style concept checking. So long as a type has function 'xxx' that takes arguments of type x,y,and z, and returns something convertible to type A, the concept is obeyed. I don't believe this can be done with C++03, but with C++1X I was able to do something using decltype.
By "named callable" do you mean a member function or static member function of the type ? TTI can check for that. Or is there some other criteria for which you are looking ?
I think a (public) member variable with an operator() also qualifies as a "named callable". Admittedly, it's not something you see often... Regards, Nate

On 7/26/2011 5:53 PM, Nathan Ridge wrote:
One thing that I think is missing is the option to check for a named callable within a type. For example, one might want to use enable_if style concept checking. So long as a type has function 'xxx' that takes arguments of type x,y,and z, and returns something convertible to type A, the concept is obeyed. I don't believe this can be done with C++03, but with C++1X I was able to do something using decltype.
By "named callable" do you mean a member function or static member function of the type ? TTI can check for that. Or is there some other criteria for which you are looking ?
I think a (public) member variable with an operator() also qualifies as a "named callable". Admittedly, it's not something you see often...
You mean an operator in general which is a member function or static function of a class ? Or just a function object operator ? I had not thought about that, but it might be possible to see if that exists also. I will look into that possibility for TTI. However Frederick Bron's extension to type traits, which is called type traits operators, does find out whether a particular operator exists for a given type. I am not sure offhand whether or not it supports the call operator '()' for function objects or not. Eddie
participants (6)
-
Edward Diener
-
Jeffrey Lee Hellrung, Jr.
-
John Bytheway
-
Mathias Gaunard
-
Nathan Ridge
-
Noah Roberts