Constraints
Template constraints allow you to specify requirements that template type parameters must satisfy, providing compile-time guarantees that template arguments have the necessary methods or properties.
Benefits
- Type Safety: Catch errors at compile time instead of link time
- Better Error Messages: Clear messages about what's missing
- Self-Documenting: Constraints serve as documentation
- Interface Enforcement: Ensure types conform to expected interfaces
Constraint Declaration
xxml
[ Constraint <ConstraintName> <T Constrains None> (T a)
Require (F(ReturnType)(methodName)(*) On a)
]Syntax Breakdown
[ Constraint <Name>- Start constraint declaration<T Constrains None>- Template parameters for the constraint(T a)- Parameter bindings (maps template paramTto identifiera)Require (...)- One or more requirement statements- Close with
]
Requirement Types
Method Requirements
Require that a type has specific methods with particular signatures:
xxml
Require (F(ReturnType)(methodName)(*) On paramName)F- Indicates a function/method requirement- First parentheses - Return type
- Second parentheses - Method name
- Third parentheses -
*for any parameters, or specific parameter types On paramName- Which template parameter this applies to
xxml
// Require a method named "toString" returning String^
Require (F(String^)(toString)(*) On a)
// Require a method named "run" returning Integer^
Require (F(Integer^)(run)(*) On a)
// Require a method with specific parameter types
Require (F(Bool^)(equals)(T^) On a)
// Require a method returning None (void)
Require (F(None)(doSomething)(*) On a)Constructor Requirements
Require that a type has specific constructors:
xxml
// Default constructor
Require (C(None) On a)
// Constructor taking an Integer
Require (C(Integer^) On a)
// Constructor taking multiple parameters
Require (C(String^, Integer^) On a)Truth Requirements
Require that a boolean expression is true at compile time:
xxml
// Require that type T is exactly SomeClass
Require (Truth(TypeOf<T>() == TypeOf<SomeClass>()))Using Constraints
Step 1: Define a Constraint
xxml
[ Constraint <Printable> <T Constrains None> (T a)
Require (F(String^)(toString)(*) On a)
]Step 2: Apply to Template Class
xxml
// Single constraint
[ Class <Printer> <T Constrains Printable> Final Extends None
// Single constraint with template arguments (two syntaxes)
[ Class <HashSet> <T Constrains Hashable<T>> Final Extends None
[ Class <HashSet> <T Constrains Hashable@T> Final Extends None
// Multiple constraints with AND semantics (must satisfy ALL)
[ Class <HashMap> <K Constrains (Hashable<K>, Equatable@K)> Final Extends None
// Multiple constraints with OR semantics (must satisfy at least ONE)
[ Class <FlexibleContainer> <T Constrains Printable | Comparable> Final Extends NoneNote
Use parentheses with commas for AND semantics (all constraints must be satisfied). Use pipe
| for OR semantics (at least one must be satisfied).Step 3: Instantiate with Valid Types
xxml
// Integer has toString(), so this works
Instantiate Printer<Integer>^ As <intPrinter> = Printer@Integer::Constructor();
// String has toString(), so this works
Instantiate Printer<String>^ As <strPrinter> = Printer@String::Constructor();Complete Example: Printable Constraint
printable.xxml
[ Constraint <Printable> <T Constrains None> (T a)
Require (F(String^)(toString)(*) On a)
]
[ Class <Logger> <T Constrains Printable> Final Extends None
[ Public <>
Property <items> Types Collections::List<T>^;
Constructor Parameters() -> {
Set items = Collections::List@T::Constructor();
}
Method <add> Returns None Parameters (Parameter <item> Types T^) Do {
Run items.add(item);
}
Method <logAll> Returns None Parameters () Do {
For (Integer <i> = 0 .. items.size()) -> {
Instantiate T^ As <item> = items.get(i);
Run Console::printLine(item.toString());
}
}
]
]Common Constraint Patterns
Comparable
xxml
[ Constraint <Comparable> <T Constrains None> (T a)
Require (F(Bool^)(lessThan)(T^) On a)
Require (F(Bool^)(equals)(T^) On a)
]Sortable
xxml
[ Constraint <Sortable> <T Constrains None> (T a)
Require (F(Bool^)(lessThan)(T^) On a)
Require (F(Bool^)(greaterThan)(T^) On a)
Require (F(Bool^)(equals)(T^) On a)
Require (F(String^)(toString)(*) On a)
]Hashable (from STL)
xxml
[ Constraint <Hashable> <T Constrains None> (T a)
Require (F(NativeType<"int64">^)(hash)(*) On a)
]Numeric
xxml
[ Constraint <Numeric> <T Constrains None> (T a)
Require (F(T^)(add)(T^) On a)
Require (F(T^)(subtract)(T^) On a)
Require (F(T^)(multiply)(T^) On a)
Require (F(T^)(divide)(T^) On a)
]Constraint Syntax Summary
| Syntax | Meaning |
|---|---|
<T Constrains None> | No constraints |
<T Constrains Printable> | Single constraint |
<T Constrains Hashable<T>> | With template args (angle brackets) |
<T Constrains Hashable@T> | With template args (at-sign) |
<T Constrains (A, B)> | AND semantics (must satisfy all) |
<T Constrains A | B> | OR semantics (satisfy at least one) |
Error Handling
If you try to use a type that doesn't satisfy the constraint, you'll get a clear compile-time error:
text
Error: Type 'MyClass' does not satisfy constraint 'Printable'
Required method: toStringTip
Add the required method to your class to satisfy the constraint. Ensure the method has the correct name, return type, and parameter types.
Best Practices
- Use minimal requirements: Only require what you actually need
- Clear naming: Use descriptive constraint names like
SortablenotS - Document requirements: Add comments explaining why each requirement exists
- Test with multiple types: Verify constraints work with various types
Next Steps
Learn about Generics for template programming, or explore Classes to understand how constraints are applied.