- Removal of the Concepts part.
The use of Boost.ConceptCheck to emulate C++ concepts proposal is in my
opinion erroneous. Boost.Concept.Check is used to assert the actual
parameter satisfy statically the concept conditions, while if I'm not
wrong
the C++ Concepts proposal use the requires clause to state the
conditions
of
a possible instantiations. Using the same grammar with different
semantics
is confusing. In a last resort, if this feature is preserved, the
requires
keyword must be changed by another one.If that were to be the final decision, I'll remove the interface to
Boost.ConceptCheck or I can use any (alphanumeric) mane rather than
requires (e.g., where).
However, I can use the requires clausule to do multiple things:
1. Check concepts defined using Boost.ConceptCheck (current, I'd leave
it as is).
2. Check static_assert (under consideration, to be moved here from
within pre/post/inv).
3. Check Boost.Generic-like concepts implemented using C++11
expression SFINAE (for future... this will be a lot of work...).
I should be able to support of all those because I can distinguish 2
using the pp (starts with static_assert)
I will really prefer the static_assert goes outside requires, e.g. in a
new
check clause. This clause could also contain the ConceptCheck part. In
this way it is clear that the requires part will disable the
instantiation
if not satisfied, and the check part will report a compile-time error if
not
satisfied.and I can distinguish between
1 and 2 using the compiler for example using a (hidden) tagging base
class to all concepts defined for 3.I'm not sure this distinction is possible. For function templates, the
requires part associated to ConceptCheck should be included in the
function
definition, while the one associated to enable_if/SFINAE c++ proposal
concepts emulation should be part of the C++ declaration. For template
classes, ConceptCheck goes to the members part, while Concepts goes to
the
template parameter part.
As you can see the semantics are different, so a specific keyword would
help
to identify the difference.
Can you provide an example of how all of this would ideally look like?
You can also pick a "keyword" other than requires that makes sense to
you and then indicate where to use requires and where to use the other
keyword in the example. Maybe you start from the static_assert example
I gave:
template< typename To, typename From >
requires // new concepts??
Convertible<From, To> // disable this declaration using
SFINAE??
unless // static_assert and Boost.ConceptCheck??
static_assert(sizeof(To) >= sizeof(From), "destination too
small"), // generate compiler-error??
boost::Copyable<From> // generate compiler-error??
To memcopy ( To to, From* from )
precondition {
to; // pointer not null
from; // pointer not null
}
{
// ...
}
I will use check as keyword for the static_assert and ConceptCheck part
template< typename To, typename From >
requires // used with enable_if in the declaration part
is_convertible<From, To>::value
check // included in the function body
assert(sizeof(To) >= sizeof(From), "destination too small")
&& boost::Copyable<From>
To memcopy ( To to, From* from )
precondition {
to; // pointer not null
from; // pointer not null
}
{
// ...
}
You could also use the check
sizeof(To) >= sizeof(From)
in the requires part if you wanted to disable overload resolution when the
condition fails as in
template< typename To, typename From >
requires // used with enable_if in the declaration part
is_convertible<From, To>::value
&& sizeof(To) >= sizeof(From)
check // included in the function body
boost::Copyable<From>
To memcopy ( To to, From* from )
// ...
Note however that the Concept Copyable from ConceptCheck can not be used in
the requires clause (I'm looking for), it is not a bool metafunction (or a
trait). It just compile fail when the parameter doesn't models the concept.
This makes sense to me and, as far as I can tell, it is also in line
with Andrzej suggestion. I'm happy to implement it this way.
CONTRACT_FUNCTION(
template( typename To, typename From )
requires( // used with enable_if in the declaration part
is_convertible<From, To>::value,
is_convertible<To, From>::value)
check( // included in the function bodystatic_assert(sizeof(To) >= sizeof(From), "destination too small"),
boost::Copyable<From>)
(To) memcopy ( (To) to, (From*) from )
precondition(
to; // pointer not null
from; // pointer not null)
) {
// ...
}
I will implement the check clausule now and the require one later and
only if I'm able to extend the lib to declare and check
Boost.Generic-like concepts.
Just to consider all alternatives, we could also do this:
CONTRACT_FUNCTION(
template( typename To, typename From )
if( // used with enable_if in the declaration part
is_convertible<From, To>::value,
is_convertible<To, From>::value)
requires( // included in the function bodystatic_assert(sizeof(To) >= sizeof(From), "destination too small"),
boost::Copyable<From>)
(To) memcopy ( (To) to, (From*) from )
precondition(
to; // pointer not null
from; // pointer not null)
) {
// ...
}
Given that the 1st clausule is more like enable-if and the 2nd
clausule states "hard" requirements (in that you get a "hard" compiler
errors when the requirements are not met). But I'm happy with
requires/check and if/requires the same way (and I still haven't read
N3331 which might suggest a syntax vs another...).
The requires clausule will be saved for C++11/enable_if/expression-SFINAE/Boost.Generic-like concepts (no hard errors but removal from overload resolution).
static_assert will not be allowed in pre/post/class-inv/block-inv/loop-var/etc but only in the check clausule.
But for those "static preconditions" there exist better tools. For instance
"static if" proposed for C++17:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3329.pdf
In section 2.4 it describes sort of "small concepts". Using this proposal,
your example could be rewritten to:
I am terribly sorry. I made a type-o. In fact I meant n3351 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3351.pdf): "A Concept Design for the STL", edited By Bjarne Stroustrup and Andrew Sutton.
There are 3 different static checks for function/class templates:
1. enable-if: declaration removed if static boolean condition is false. Not necessarily compiler-error. There will be a compiler-error only if there's no other declaration that can be used to correctly instantiate the template.
2. static_assert: Always compiler-error if static boolean condition is false. Allowing this removes the need to support static_assert in preconditions, postconditions, and invariants (and correctly so because contract assertions are associated with the run-time).
3. Boost.Concept: Always compiler-error if concepts are not modeled. But these type of concepts cannot always be programmed as static boolean conditions using C++.
The conditions that can be expressed by 3 are a super-set than the conditions that can be expressed by 2. Specifically, any static boolean condition that can be expressed by 2 like boolean_cond<T, U, ...>::value, can also be expressed using 3 using the concept True below like True<boolean_cond<T, U, ...>>. Therefore, 2 is not needed and can be implemented via True using 3 instead.
For 1, I would use "static if" instead of "if" as proposed by N3329 because N3329 uses "static if" with the same meaning within definitions and I'd use the same "static if" in the declarations. An alternative would be "static_if" (in both decls and defs) to be more consistent with "static_assert". Another alternative would be to use "enable_if" (in both decls and defs) whose meaning is already familiar to C++ programmers. However, N3329 suggests "static if" (like in D) so I'd use that (plus both static_if and enable_if might make the optional else(...) statement, which is allowed only in definitions, look confusing).
Furthermore, I'd use "static if" after template<> and not after the function parameters as suggested by N3329. This library can't use the function argument names decltype(arg_name) anyway and that'd be the only reason to use "static if" after the function parameters.
For 3, I'd use "concept_assert" which is similar to "static_assert" but checks (Boost) concepts instead of static boolean conditions (plus static boolean conditions wrapped by the Boost concept True).
Again, I'd use "concept_assert" after template<> instead of after the function parameters because this library cannot use the function arguments decltype(arg_name) and because that's where "requires" is used to enforce (check and/or enable_if) concepts.
Also, using a static boolean expressions (like the one passed to static_assert) instead of a boolean meta-function:
However, True<boost::mpl::bool_< ... > > will always report True<boost::mpl::bool_<false> > when it fails so the error is not going to be as informative as when a mata-function is used (the meta-function call is more informative in that case).
I should provide an implementation of True, something like contract::True from contract/concept/true.hpp.
Provide also contract::False from contract/concept/false.hpp for symmetry.
N3351 defines True (and maybe False) as a concept that is always true (false). These concepts instead require that the specified meta-function evaluates to true (false) so they are different. Maybe it makes more sense to name them:
contract::IsTrue<B> from contract/concept/is_true.hpp
contract::IsFalase<B> from contract/concept/is_false.hpp