ITK/Proposals:Concept Checking
Introduction
One of the characteristics of Generic Programming is that classes and methods can be instantiated over any type. This freedom however, is still restricted by a number of characteristics that those types must satisfy in order to work properly with the classes they are instantiating.
For example, a templated class has template parameter T and internally perform addition operations on that type, will require T to have an operator+() defined, or to be a basic type such as int, float or char, for which such operator is already defined in the language.
When a templated class is instantiated over a type that do no satisfy the requirement, most compilers will generate error messages that are difficult to interpret. It is then desirable to have a mechanism for making those messages simpler or at least easier to understand for the users of the software.
Concept checking is an approach by which language structures are introduced in templated classes in order to test early for the expected characteristics of a template parameter. When a user instantiate this templated class over a type that does not satisfy the concept, then the compiler will generate an error message that is easier to interpret, and that will convey to the user the message that the type is not an appropriate template argument for this class.
Macro Implementation
Implementation Mechanism
The implementation mechanism of concept checking in ITK is inspired on the method used by the BOOST library
http://www.boost.org/libs/concept_check/concept_check.htm
This is based in a three stages process
- Auxiliary classes that exercise a concept. ("Constraints")
- Auxiliary classes that instantiate the Checkers. ("Instantiators")
- Macros that name the concept and insert the Instantiators.
Example
In order to illustrate this mechanism, a simple concept checking sequence is presented here, for the case of multiplication. This is a case in which a templated class of type
template <typename T> class ImageFilter< T >
internally performs multiplication operations with variables of type "T".
The Constraint class that checks for this multiplication concept is the following
struct Constraints { void constraints() { a = static_cast<T3>(b * c); const_constraints(b, c); } void const_constraints(const T1& d, const T2& e) { a = static_cast<T3>(d * e); } T3 a; T1 b; T2 c; };
This class checks that a variable of type T1 can be multiplied by a variable of type T2 and the result be assigned to a variable of Type T3.
This constraint is then embeded into a structure whose name clearly and distinctively identifies the concept. In the case of multiplication this structure is named: "MultiplyOperator", and its code looks like:
/** Concept requiring T to have operator * in the form T1 op T2 = T3. */ template <typename T1, typename T2=T1, typename T3=T1> struct MultiplyOperator { struct Constraints { ... }; itkConceptConstraintsMacro(); };
Naming of this class is critical in the working of Concept Checking because that name is the first word that will appear in the compiler error message if a user instantiate the ITK filter with a type that does not satisfy the requirement of having a multiplication operator.
List of Concepts
The following table list all the concepts for which ITK is checking in its filters.
For full implementation detail please see the file
Insight/Code/Common/itkConceptChecking.h.
Concepts
Index | Concept | Description |
---|---|---|
1 | DefaultConstructible | Concept requiring T to have a default constructor. |
2 | CopyConstructible | Concept requiring T to have a copy constructor. |
3 | Convertible | Concept requiring T1 to be convertible to T2. |
4 | Assignable | Concept requiring T to have operator =. |
5 | LessThanComparable | Concept requiring T1 to have operators < and <= with a right-hand operator of type T2. |
6 | EqualityComparable | Concept requiring T1 to have operators == and != with a right-hand operator of type T2. |
7 | Comparable | Concept requiring T1 to have operators <, >, <=, >=, ==, != with a right-hand operator of type T2. |
8 | AdditiveOperators | Concept requiring T1 to have operators +, -, +=, -= in the form T3 = T1 op T2. |
9 | MultiplyOperator | Concept requiring T1 to have operator * in the form T3 = T1 op T2. |
10 | Signed | Concept requiring T to be signed. |
11 | SameType | Concept requiring T1 and T2 to be the same type. |
12 | SameDimension | Concept requiring D1 and D2 to be the same dimension. |
13 | GreaterThanComparable | Concept requiring T1 to have operators > and >= with a right-hand operator of type T2. |
14 | LogicalOperators | Concept requiring T1 to have operators &, I, ^, &=, I=, ^= in the form T3 = T1 op T2. |
15 | NotOperator | Concept requiring T to have operator !. |
16 | IncrementDecrementOperators | Concept requiring T to have operators ++ and --. |
17 | OStreamWritable | Concept requiring T to be writable to an ostream. |
18 | HasNumericTraits | Concept requiring T to have NumericTraits. |
19 | HasPixelTraits | Concept requiring T to have PixelTraits. |
20 | HasJoinTraits | Concept requiring T to have JoinTraits. |
21 | SameDimensionOrMinusOne | Concept requiring D1 and D2 to be the same dimension or D2-1 = D2. |
22 | MultiplyAndAssignOperator | Concept requiring T1 to have operator *= in the form T2 op= T1. |
23 | DivisionOperators | Concept requiring T1 to have operators / and /= in the form T3 = T1 op T2. |
24 | IsInteger | Concept requiring T to be inteter. |
25 | IsNonInteger | Concept requiring T to be non-inteter. |
26 | IsFloatingPoint | Concept requiring T to be floating point. |
27 | IsFixedPoint | Concept requiring T to be fixed point. |
Proposed concept additions
Additional concepts for requiring integer or noninteger datatypes should be added to the toolkit. NumericTraits defines static boolean constants
- is_bounded
- is_exact
- is_iec559 (???)
- is_integer
- is_modulo
- is_signed
- is_specialized
- tinyness_before
These traits can be used to define concepts in a manner similar to the Signed concept. We should consider separating the concepts of integer, non-integer, floating point so that we can expand our native datatypes to include a fixed point representation.
The concepts below are poorly named. We should not have a concept called Integer since a user could confuse the name of the concept with a datatype. Unfortunately, none of the current concepts have the word concept in their name. We could follow NumericTraits, and make the new concepts called IsInteger.
Index | Concept | Description |
---|---|---|
13 | Integer | Concept requiring T to be an integer. |
14 | Noninteger | Concept requiring T to be noninteger (floating point or fixed point). |
15 | FloatingPoint | Concept requiring T to be floating point (not integer and not fixed point) |
16 | FixedPoint | Concept requiring T to be an ITK fixed point representation (not integer and not floating point) |
Integration into Filters
The concepts listed in the table above were introduced in ITK filters.
Please follow the link below for a detailed list of filters and their associated concepts.