| %module cpp20_concepts_classes | |
| %warnfilter(SWIGWARN_PARSE_CONSTRAINED_REDECLARATION) SelectBox; | |
| %warnfilter(SWIGWARN_PARSE_CONSTRAINED_REDECLARATION) SelectBox::kind; | |
| // C++20 concepts applied to class templates: prefix requires-clause, trailing requires on ordinary methods, compound '&&', constrained constructor. | |
| %inline %{ | |
| #include <concepts> | |
| template<typename T> | |
| concept Numeric = std::integral<T> || std::floating_point<T>; | |
| template<typename T> | |
| concept SmallNumeric = Numeric<T> && (sizeof(T) <= 4); | |
| // Class template with a prefix requires-clause on the template head. | |
| template<typename T> | |
| requires Numeric<T> | |
| class NumericBox { | |
| T value; | |
| public: | |
| NumericBox() : value(T()) {} | |
| NumericBox(T v) : value(v) {} | |
| T get() const { return value; } | |
| void set(T v) { value = v; } | |
| T cube() const { return value * value * value; } | |
| }; | |
| // Unconstrained class template whose ordinary methods carry their own trailing requires-clauses referencing the class's template parameter. | |
| template<typename T> | |
| class Holder { | |
| T value; | |
| public: | |
| Holder() : value(T()) {} | |
| Holder(T v) : value(v) {} | |
| T get() const { return value; } | |
| void set(T v) { value = v; } | |
| // Trailing requires-clause on an ordinary method. | |
| T doubled() const requires Numeric<T> { return value + value; } | |
| }; | |
| // Class template with a compound prefix requires-clause joined by '&&'. | |
| template<typename T> | |
| requires Numeric<T> && SmallNumeric<T> | |
| class SmallBox { | |
| T value; | |
| public: | |
| SmallBox() : value(T()) {} | |
| SmallBox(T v) : value(v) {} | |
| T get() const { return value; } | |
| }; | |
| // Class template with a constrained constructor (the class is otherwise unconstrained, but only | |
| // Numeric T values can construct from a value). The constructor's trailing requires-clause is held | |
| // on the constructor node's "constraint" attribute and is substituted when the class template is | |
| // instantiated; both 'int' and 'double' satisfy 'Numeric<T>' so both instantiations succeed. | |
| template<typename T> | |
| class CheckedBox { | |
| T value; | |
| public: | |
| CheckedBox() : value(T()) {} | |
| CheckedBox(T v) requires Numeric<T> : value(v) {} | |
| T get() const { return value; } | |
| }; | |
| // Members of a class template that differ only by a trailing requires-clause are ignored with warning 333 | |
| // and only the first is wrapped, but the wrapper names the member rather than one of the two declarations, so | |
| // the C++ compiler still selects the overload each instantiation satisfies. A %template naming a type that | |
| // satisfies neither constraint is a C++ compiler error on the generated wrapper, not something SWIG reports. | |
| template<typename T> | |
| class SelectBox { | |
| T value; | |
| public: | |
| SelectBox(T v) requires std::integral<T> : value(v + 1) {} | |
| SelectBox(T v) requires std::floating_point<T> : value(v + 2) {} | |
| int kind() const requires std::integral<T> { return 1; } | |
| int kind() const requires std::floating_point<T> { return 2; } | |
| T get() const { return value; } | |
| }; | |
| // Out of line definition of a member function template of a class template, | |
| // with a prefix requires-clause on its own template head. Exercises the | |
| // requires_clause_opt path on a doubly templated declaration. | |
| template<typename T> | |
| class OutOfLineBox { | |
| T value; | |
| public: | |
| OutOfLineBox(T v) : value(v) {} | |
| T get() const { return value; } | |
| template<typename U> requires Numeric<U> | |
| T scaled(U factor) const; | |
| }; | |
| template<typename T> | |
| template<typename U> requires Numeric<U> | |
| T OutOfLineBox<T>::scaled(U factor) const { return value * (T)factor; } | |
| // Primary class template plus a structural partial specialization (T -> T*) whose template head additionally carries | |
| // a requires-clause. SWIG's partial spec matcher selects on the structural pattern alone; the requires-clause is | |
| // captured but not evaluated. The two specs wrap as distinct types with their own method sets - primary_method only | |
| // reaches Storage<int>, partial_method only reaches Storage<int*>. | |
| // | |
| // Limitation: SWIG does not select between candidates whose structural template-argument patterns are identical and | |
| // which differ only by a requires-clause, e.g. | |
| // | |
| // template<typename T> struct S { ... }; // primary | |
| // template<typename T> requires Numeric<T> struct S<T> { ... }; // concept only "partial spec" | |
| // | |
| // SWIG sees both as 'S<T>' and the last declared candidate wins for every instantiation; C++20 constraint | |
| // subsumption is not modelled. Use a structural pattern to differentiate (T vs T*, T const&, etc.) when partial | |
| // specs need to be wrapped distinctly. | |
| template<typename T> | |
| struct Storage { | |
| int kind() const { return 1; } | |
| int primary_method() const { return 100; } | |
| }; | |
| template<typename T> requires Numeric<T> | |
| struct Storage<T*> { | |
| int kind() const { return 2; } | |
| int partial_method() const { return 200; } | |
| }; | |
| // Concept constrained CRTP - the base class is the constrained template parameter, so the prefix | |
| // requires-clause sits between the template head and a base list that names that parameter. Verifies | |
| // the constraint does not interfere with the inheritance / base list path. | |
| template<typename T> | |
| concept HasDraw = requires(T t) { t.draw(); }; | |
| struct Drawable { | |
| int counter = 0; | |
| void draw() { ++counter; } | |
| }; | |
| template<typename Derived> requires HasDraw<Derived> | |
| class View : public Derived { | |
| public: | |
| void render() { this->draw(); this->draw(); } | |
| }; | |
| %} | |
| %template(NumericBoxInt) NumericBox<int>; | |
| %template(NumericBoxDouble) NumericBox<double>; | |
| %template(HolderInt) Holder<int>; | |
| %template(HolderDouble) Holder<double>; | |
| %template(SmallBoxInt) SmallBox<int>; | |
| %template(CheckedBoxInt) CheckedBox<int>; | |
| %template(CheckedBoxDouble) CheckedBox<double>; | |
| %template(SelectBoxInt) SelectBox<int>; | |
| %template(SelectBoxDouble) SelectBox<double>; | |
| %template(OutOfLineBoxInt) OutOfLineBox<int>; | |
| %template(StorageInt) Storage<int>; | |
| %template(StorageIntPtr) Storage<int*>; | |
| %template(ViewDrawable) View<Drawable>; |