
Hello all, Main purposes of concepts are to (a) document the type requirements and (b) generate useful error messages when a type does not met the requirements. I'd like to discuss how the ideal error messages should look like when a given type does not model the specified concepts. One point of discussion is: In case a type doesn't model a concept, should the compiler generate an hard error or the function/class template should simply be removed from the overload/specialization set? For example, consider: template< typename T> requires EqualityComparable<T> bool equal ( T a, T b ) // #1 { return a == b; // #3 } ... struct x { explicit x(char const*); }; x abc("abc"); equal(abc, abc); // #2 Let's assume we want an hard error then I think ideally the compiler should generate two errors: 1) An error at line #1 indicating that the concept EqualityComparable<T> is not modeled. Ideally this error will read: line #1: failed to model concept EqualityComparable<T> 2) An error at line #2 indicating that the call equal(abc, abc) cannot be resolved. Ideally this error will read: line #2: no matching call for function equal 3) Note that no error should be generated from within the equal function implementation (line #3, etc) because the concept is not modeled so the function definition should not be compiled. Now, this is what I was able to actually implement. === Option A) Using enable_if === template< typename T > typename std::enable_if< AlwaysTrue<T>::value && contract::std::EqualityComparable<T>::value, bool>::type equal1 ( T const& a, T const& b ) { return a == b; } ... equal1(abc, abc); // 08.cpp:44 08.cpp:44:5: error: no matching function for call to 'equal1' equal1(abc, abc); ^~~~~~ [...] This error is not ideal because it only satisfies 2) and 3) but not 1). It does not give information about which concept was not modeled -- you don't see EqualityComparable or "concept failed" anywhere in the error. (Clang actually mentions EqualityComparable in a "note" following the error (see below) but that's not a message that can be relied on across different compilers.) Users can use this approach if they don't want an hard error but they only want to remove the function from the overload set on concept failure. === Option B) Using concept_check === template< typename T > typename contract::concept_check< AlwaysTrue<T>::value, contract::std::EqualityComparable<T>::value, bool>::type equal2 ( T const& a, T const& b ) { return a == b; } ... equal2(abc, abc); // 08.cpp:45 In file included from 08.cpp:5: include/contract/concept_check.hpp:61:5: error: static_assert failed "model concept number 2" static_assert(Concept2, "model concept number 2"); ^ ~~~~~~~~ [...] 08.cpp:45:5: error: no matching function for call to 'equal2' equal2(abc, abc); ^~~~~~ This also satisfies 2) and 3) however it only partially satisfies 1). The error mentions the number of the concept "2" but not its name EqualityComparable (that's probably OK) but the error line number refers to the file implementing concept_check and not to the point of declaration of equal2. In other words, the error for 1) does not indicate line #1 (that's probably acceptable but not ideal). Note that the error for 1) is generate using a static_assert so it is portable. === Option C) Using CONCEPT_CHECK macro === CONTRACT_CONCEPT_CHECK( // 08.cpp:26 (template< typename T >), (AlwaysTrue<T>) (contract::std::EqualityComparable<T>), bool, equal3 ) ( T const& a, T const& b ) { return a == b; } ... equal3(abc, abc); // 08.cpp:45 08.cpp:26:1: error: static_assert failed "model concept number 2: contract::std::EqualityComparable<T>" CONTRACT_CONCEPT_CHECK( ^~~~~~~~~~~~~~~~~~~~~~~ [...] 08.cpp:46:5: error: no matching function for call to 'equal3' equal3(abc, abc); ^~~~~~ [...] These errors satisfy all 1), 2), and 3) however the use of macros makes clang generate lots of "notes" to backtrace the macro expansion (there's probably a way to disable these notes, plus they are not actual errors and can be ignored). Note that the concept name EqualityComparable appears in the static assertion error message so it is guaranteed to be printed portably among compilers. I'm thinking that these are all valid ways to check concepts. Eventually, Boost.Contract macros will use B) (given that the lib already uses macros to declare functions). However, if users want to use Boost.Contract macros to declare a concept but they want to use enable_if or contract::concept_check to check it, they can do so (in case they don't want to use the lib macros to declare their functions). Complete error messages (including "notes"): 08.cpp:44:5: error: no matching function for call to 'equal1' equal1(abc, abc); ^~~~~~ 08.cpp:12:5: note: candidate template ignored: disabled by 'enable_if' [with T = x] AlwaysTrue<T>::value && contract::std::EqualityComparable<T>::value, ^ In file included from 08.cpp:5: include/contract/concept_check.hpp:61:5: error: static_assert failed "model concept number 2" static_assert(Concept2, "model concept number 2"); ^ ~~~~~~~~ 08.cpp:19:20: note: in instantiation of template class 'contract::concept_check<true, false, bool>' requested here typename contract::concept_check< ^ 08.cpp:21:13: note: while substituting deduced template arguments into function template 'equal2' [with T = x] bool>::type equal2 ( T const& a, T const& b ) ^ 08.cpp:45:5: error: no matching function for call to 'equal2' equal2(abc, abc); ^~~~~~ 08.cpp:21:13: note: candidate template ignored: substitution failure [with T = x] bool>::type equal2 ( T const& a, T const& b ) ^ 08.cpp:26:1: error: static_assert failed "model concept number 2: contract::std::EqualityComparable<T>" CONTRACT_CONCEPT_CHECK( ^~~~~~~~~~~~~~~~~~~~~~~ include/contract/concept_check.hpp:40:33: note: expanded from macro 'CONTRACT_CONCEPT_CHECK' BOOST_PP_SEQ_FOR_EACH_I(CONTRACT_CONCEPT_CHECK_ASSERT_, ~, concepts) \ ^ ../../../boost_1_50_0.cyg/boost/preprocessor/seq/for_each_i.hpp:27:69: note: expanded from macro 'BOOST_PP_SEQ_FOR_EACH_I' # define BOOST_PP_SEQ_FOR_EACH_I(macro, data, seq) BOOST_PP_FOR((macro, data, seq (nil), 0), BOOST_PP_SEQ_FOR_EACH_I_P, BOOST_PP_SEQ_FOR_EACH_I_O, BOOST_PP_SEQ_FOR_EACH_I_M) ^ ../../../boost_1_50_0.cyg/boost/preprocessor/repetition/detail/for.hpp:22:78: note: expanded from macro 'BOOST_PP_FOR_1' # define BOOST_PP_FOR_1(s, p, o, m) BOOST_PP_FOR_1_C(BOOST_PP_BOOL(p(2, s)), s, p, o, m) ^ note: (skipping 7 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all) ../../../boost_1_50_0.cyg/boost/preprocessor/seq/for_each_i.hpp:45:80: note: expanded from macro 'BOOST_PP_SEQ_FOR_EACH_I_M_IM' # define BOOST_PP_SEQ_FOR_EACH_I_M_IM(r, im) BOOST_PP_SEQ_FOR_EACH_I_M_I(r, im) ^ ../../../boost_1_50_0.cyg/boost/preprocessor/seq/for_each_i.hpp:50:62: note: expanded from macro 'BOOST_PP_SEQ_FOR_EACH_I_M_I' # define BOOST_PP_SEQ_FOR_EACH_I_M_I(r, macro, data, seq, i) macro(r, data, i, BOOST_PP_SEQ_HEAD(seq)) ^ include/contract/concept_check.hpp:23:5: note: expanded from macro 'CONTRACT_CONCEPT_CHECK_ASSERT_' static_assert(CONTRACT_CONCEPT_CHECK_COND_(i), "model concept number " \ ^ 08.cpp:26:1: note: in instantiation of template class 'contractXconceptXcheckXequal3X30<true, false>' requested here CONTRACT_CONCEPT_CHECK( ^ include/contract/concept_check.hpp:50:14: note: expanded from macro 'CONTRACT_CONCEPT_CHECK' typename CONTRACT_CONCEPT_CHECK_CLASS_(func_name)< \ ^ include/contract/concept_check.hpp:11:23: note: expanded from macro 'CONTRACT_CONCEPT_CHECK_CLASS_' BOOST_PP_SEQ_CAT((contractXconceptXcheckX)(func_name)(X)(__LINE__)) ^ ../../../boost_1_50_0.cyg/boost/preprocessor/seq/cat.hpp:30:7: note: expanded from macro 'BOOST_PP_SEQ_CAT' )(seq) \ ^ note: (skipping 18 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all) ../../../boost_1_50_0.cyg/boost/preprocessor/seq/cat.hpp:35:37: note: expanded from macro 'BOOST_PP_SEQ_CAT_O_I' # define BOOST_PP_SEQ_CAT_O_I(a, b) a ## b ^ <scratch space>:91:1: note: expanded from macro 'contractXconceptXcheckXequal3X' contractXconceptXcheckXequal3X30 ^ ../../../boost_1_50_0.cyg/boost/preprocessor/seq/fold_left.hpp:295:51: note: expanded from macro 'BOOST_PP_SEQ_FOLD_LEFT_F' # define BOOST_PP_SEQ_FOLD_LEFT_F(op, st, ss, sz) st ^ 08.cpp:29:11: note: while substituting deduced template arguments into function template 'equal3' [with T = x] bool, equal3 ^ include/contract/concept_check.hpp:53:5: note: expanded from macro 'CONTRACT_CONCEPT_CHECK' func_name ^ 08.cpp:46:5: error: no matching function for call to 'equal3' equal3(abc, abc); ^~~~~~ 08.cpp:29:11: note: candidate template ignored: substitution failure [with T = x] bool, equal3 ^ include/contract/concept_check.hpp:53:5: note: expanded from macro 'CONTRACT_CONCEPT_CHECK' func_name ^ 5 errors generated. --Lorenzo