[contract] SFINAE question for concepts

Hello all, I have a SFINAE question. The following correctly returns false for eq1<x>::value when x has no operator== template< typename T > struct eq1 { T a; T b; template< typename TX > static char check ( TX a, TX b, char(*)[sizeof (std::is_convertible<decltype(a == b), bool>) ]); template< typename TX > static long check ( ... ); static const bool value = sizeof(char) == sizeof check<T>(a, b, 0); }; However, the following implementation that uses bool{a == b} incorrectly returns true for eq2<x>::value even if x has no operator==... why? BTW, if I just use (a == b) instead of bool{a == b} then it works... template< typename T > struct eq2 { T a; T b; template< typename TX > static char check ( TX a, TX b, char(*)[sizeof bool{a == b} ]); template< typename TX > static long check ( ... ); static const bool value = sizeof(char) == sizeof check<T>(a, b, 0); }; A complete example is attached. I'm using clang++ 3.2 with -std=c++0x. Thanks, --Lorenzo

On Wed, Oct 3, 2012 at 4:02 PM, Lorenzo Caminiti <lorcaminiti@gmail.com>wrote:
Hello all,
I have a SFINAE question. The following correctly returns false for eq1<x>::value when x has no operator==
I encountered the same problem in GCC when developing Generic, only with constructor-style initialization at the time -- I thought it was just a compiler bug, but since Clang is doing it too, maybe it's not. What I assumed was happening was that since it's in an unevaluated context, the compiler realized it could get the sizeof the expression without needing to know anything about the type of the subexpression, so it [incorrectly] only did syntax checking on it. In other words, it short-circuits before doing substitution so SFINAE never rules it out. However, I'm testing it now when not inside of a place where SFINAE may occur (though still with template arguments), and I correctly get an error when the template is instantiated. This is true with both GCC and Clang, so it seems like this unintuitive behavior may, in fact, be standard. I don't have time to check it out right now, but if someone references the standard and still can't figure out why you're getting this behavior, you could always submit a bug report to Clang. They're versed enough in standardese to determine if it's standard behavior or not. I'd say that you could use a workaround, but I assume you want the {} syntax so that you could do the N3351 syntax for expression validation directly. Also, I'm going to warn you now because I went down this same exact path with Generic -- you're going to have to change your approach for representing givens -- datamembers and even function parameters will not work. The problem is, you need to account for checking of "void", but with this implementation, you'll get a hard error instead of a compile-time bool value. You need to get everything into a context where SFINAE can take place. My approach with the old-style generic was to do all of the checking in a template specialization pattern list (as opposed to in a function template definition), and I used template reference parameters for the givens. -- -Matt Calabrese

On Wed, Oct 3, 2012 at 7:41 PM, Matt Calabrese <rivorus@gmail.com> wrote:
Also, I'm going to warn you now because I went down this same exact path with Generic -- you're going to have to change your approach for representing givens -- datamembers and even function parameters will not work. The problem is, you need to account for checking of "void", but with this implementation, you'll get a hard error instead of a compile-time bool value. You need to get everything into a context where SFINAE can take place. My approach with the old-style generic was to do all of the checking in a template specialization pattern list (as opposed to in a function template definition), and I used template reference parameters for the givens.
I'm not sure how much help it'll be, but you can look at the .hei files in Boost.Generic's std_concept headers to see how I do the checks exactly. All the tests for all of my concepts explicitly check results when using void for each of the concept arguments, along with other "problem" types (such as function types, since a function type that appears in a function parameter list is treated as a function pointer type). The approach I use should work for your N3351 concepts implementation as well, but it might not be worth trying to figure out the preprocessor output. Anyway, my recommendation is just make all of your givens template arguments (not function arguments) and do all of your SFINAE stuff as a part of a specialization of that template. -- -Matt Calabrese

On Wed, Oct 3, 2012 at 4:41 PM, Matt Calabrese <rivorus@gmail.com> wrote:
On Wed, Oct 3, 2012 at 4:02 PM, Lorenzo Caminiti <lorcaminiti@gmail.com>wrote:
Hello all,
I have a SFINAE question. The following correctly returns false for eq1<x>::value when x has no operator==
I encountered the same problem in GCC when developing Generic, only with constructor-style initialization at the time -- I thought it was just a compiler bug, but since Clang is doing it too, maybe it's not. What I assumed was happening was that since it's in an unevaluated context, the compiler realized it could get the sizeof the expression without needing to know anything about the type of the subexpression, so it [incorrectly] only did syntax checking on it. In other words, it short-circuits before doing substitution so SFINAE never rules it out.
I noticed that wrapping the bool{} in a decltype works*: #define REQUIRES_T_a_b_c(n, expr) \ struct BOOST_PP_CAT(requires, n) \ { \ T a; T b; T c; \ template< typename TX > static boost::yes_type check ( \ TX a, TX b, char(*)[sizeof \ (decltype(expr)) \ ] \ ); \ template< typename TX > static boost::no_type check ( ... ); \ static const bool value = sizeof(boost::yes_type) == \ sizeof check<T>(a, b, 0); \ }; template< typename T > struct EqualityComparable { REQUIRES_T_a_b_c(0, bool{a == b}) REQUIRES_T_a_b_c(1, bool{a != b}) static const bool value = requires0::value && requires1::value; }; But I still would like to understand why it doesn't work without the decltype... (*) I'm just playing around now with some example so "works" has a very limited meaning at the moment. This way, I should be able to handle expressions... for types, I'm thinking require a typename prefix in the concept declaration syntax so I know not to use the decltype() in the code expanded by the macros: CONTRACT_CONCEPT( concept (Common) ( typename T, typename U ) ( requires( typename (CommonType<T, U>) // typename so I know this is a type and not an expression and expanded code won't use decltype ) ) )
However, I'm testing it now when not inside of a place where SFINAE may occur (though still with template arguments), and I correctly get an error when the template is instantiated. This is true with both GCC and Clang, so it seems like this unintuitive behavior may, in fact, be standard. I don't have time to check it out right now, but if someone references the standard and still can't figure out why you're getting this behavior, you could always submit a bug report to Clang. They're versed enough in standardese to determine if it's standard behavior or not.
I'd say that you could use a workaround, but I assume you want the {} syntax so that you could do the N3351 syntax for expression validation directly.
Also, I'm going to warn you now because I went down this same exact path with Generic -- you're going to have to change your approach for representing givens -- datamembers and even function parameters will not work. The problem is, you need to account for checking of "void", but with this implementation, you'll get a hard error instead of a compile-time bool value. You need to get everything into a context where SFINAE can take place. My approach with the old-style generic was to do all of the checking in a template specialization pattern list (as opposed to in a function template definition), and I used template reference parameters for the givens.
Yep, I'm not sure about data/function members... I'll definitely look at Boost.Generic source! Thanks, --Lorenzo
participants (2)
-
Lorenzo Caminiti
-
Matt Calabrese