.. include:: <mmlalias.txt> Writing Your Own Concepts ========================= .. contents:: :local: Starting Point: Good Old Function --------------------------------- * N-dimensional hypotenuse, over ``std::vector<double>`` * Uses * ``v.size()`` * ``v.operator[]()`` .. literalinclude:: code/hypotenuse-good-old-function.cpp :caption: :download:`code/hypotenuse-good-old-function.cpp` :language: c++ Problem: ``std::vector<double>`` Is Not Enough |longrightarrow| Template ------------------------------------------------------------------------ * ``point2d``: members ``x, y`` (and potentially some operations on a point, but that is not the point here) * |longrightarrow| Turn ``hypotenuse()`` into a template * *Straightforward* * |longrightarrow| Errors deep down in the implementation .. literalinclude:: code/hypotenuse-template-no-concept-error.cpp :caption: :download:`code/hypotenuse-template-no-concept-error.cpp` :language: c++ .. code-block:: console error: ‘const class point2d’ has no member named ‘size’ 9 | for (size_t i=0; i<v.size(); ++i) | ~~^~~~ error: no match for ‘operator[]’ (operand types are ‘const point2d’ and ‘size_t’ {aka ‘long unsigned int’}) 10 | sumsq += v[i]*v[i]; | ~^ error: no match for ‘operator[]’ (operand types are ‘const point2d’ and ‘size_t’ {aka ‘long unsigned int’}) 10 | sumsq += v[i]*v[i]; | ~^ Concept: ``has_size`` --------------------- * Implement concept ``has_size`` * Requires that any object of ``V`` has a ``.size()`` method * |longrightarrow| i.e. the expression ``v.size()`` *compiles* .. literalinclude:: code/hypotenuse-template-concept-has_size-error.cpp :caption: :download:`code/hypotenuse-template-concept-has_size-error.cpp` :language: c++ .. code-block:: console error: no matching function for call to ‘hypotenuse(point2d&)’ 31 | std::cout << hypotenuse(p) << std::endl; | ~~~~~~~~~~^~~ ... blah ... note: constraints not satisfied ... blah ... required for the satisfaction of ‘has_size<V>’ [with V = point2d] Fix ``has_size`` Failure In ``point2d`` --------------------------------------- * Fix: add ``point2d::size()`` as required * Still old-school-complains about ``operator[]`` .. literalinclude:: code/hypotenuse-template-concept-has_size-fixed.cpp :caption: :download:`code/hypotenuse-template-concept-has_size-fixed.cpp` :language: c++ Problem: What If I Pass Elements That Do Not Support ``*``? ----------------------------------------------------------- * E.g.: ``std::vector<std::string>`` * Which is nonsense because vector (in its math sense) coordinates must be *multipliable* * |longrightarrow| does not support ``operator*()`` .. literalinclude:: code/hypotenuse-coordinate-not-multipliable-error.cpp :caption: :download:`code/hypotenuse-coordinate-not-multipliable-error.cpp` :language: c++ .. code-block:: console error: no match for ‘operator*’ (operand types are ‘const __gnu_cxx::__alloc_traits<std::allocator<std::pair<int, int> >, std::pair<int, int> >::value_type’ {aka ‘const std::pair<int, int>’} and ‘const __gnu_cxx::__alloc_traits<std::allocator<std::pair<int, int> >, std::pair<int, int> >::value_type’ {aka ‘const std::pair<int, int>’}) 17 | sumsq += v[i]*v[i]; | ~~~~^~~ Concept: ``coordinate_is_multipliable`` --------------------------------------- * Again, require this explicitly * |longrightarrow| result of ``operator[]`` must support multiplication * |longrightarrow| this is exactly how the concept looks .. literalinclude:: code/hypotenuse-coordinate-not-multipliable-concept-error.cpp :caption: :download:`code/hypotenuse-coordinate-not-multipliable-concept-error.cpp` :language: c++ .. code-block:: console note: constraints not satisfied In substitution of ‘template<class V> double hypotenuse(const V&) requires (has_size<V>) && (coordinate_is_multipliable<V>) [with V = std::vector<std::pair<int, int> >]’: required from here required for the satisfaction of ‘coordinate_is_multipliable<V>’ [with V = std::vector<std::pair<int, int>, std::allocator<std::pair<int, int> > >] in requirements with ‘V v’ [with V = std::vector<std::pair<int, int>, std::allocator<std::pair<int, int> > >] note: the required expression ‘(v[0] * v[0])’ is invalid 13 | v[0]*v[0]; // <-- as a requirement, this must compile | ~~~~^~~ Concept: Require Coordinate Type To Be Double --------------------------------------------- .. sidebar:: Documentation * `std::same_as <https://en.cppreference.com/w/cpp/concepts/same_as>`__ * `Concepts library (since C++20) <https://en.cppreference.com/w/cpp/concepts>`__ * Rationale: because I can |:muscle:| * There is no real reason but to show another syntactical highlight * ... and some usage of ``<concept>`` .. literalinclude:: code/hypotenuse-coordinate-concept-must-be-double.cpp :caption: :download:`code/hypotenuse-coordinate-concept-must-be-double.cpp` :language: c++ .. code-block:: console note: constraints not satisfied In substitution of ‘template<class V> double hypotenuse(const V&) requires contains_double_likes<V> [with V = std::vector<const char*, std::allocator<const char*> >]’: required from here required for the satisfaction of ‘contains_double_likes<V>’ [with V = std::vector<const char*, std::allocator<const char*> >] in requirements with ‘V v’ [with V = std::vector<const char*, std::allocator<const char*> >] note: ‘v[0]’ does not satisfy return-type-requirement 9 | { v[0] } -> std::same_as<double>; | ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~