In C++, templates allow you to write generic code, that can handle many types. But almost always, there will be types you can't handle.
In my case, I have a callback manager that supports functions that return a void, a number, a bool or a class. In all those cases, it knows what to do. If you pass it something that's not one of those options, compilation will fail. And that's good .
The thing is, that when it fails, it tell you why in a difficult way. It 'll point to the template code and give some complex error:
[build] FAILED: callback_examples.cpp.o [build] In module callbackmanager, imported at callback_examples.cpp:8: [build] callbackmanager.cpp: In instantiation of ‘class callbackmanager::Callback@callbackmanager<char [], const int&, const int&>’: [build] callback_examples.cpp:159:64: required from here [build] 159 | callbackmanager::Callback<char[] , const int&, const int&> cb;
Although this is correct, it's hard to find out what's wrong. It would be more useful if the error informs the developer that the code is incompatible with the template (template constraint failure). That's what a concept does:
[build] FAILED: callback_examples.cpp.o [build] callback_examples.cpp: In function ‘int main()’: [build] callback_examples.cpp:159:61: error: template constraint failure for ‘template<class R, class ... Args> requires Callbackable<R> class callbackmanager::Callback@callbackmanager’ [build] 159 | callbackmanager::Callback<char[], const int&, const int&> cb;
It doesn't make programs better, nor does it add to the size of the executable code. But it helps developers to use your generic code correctly.
Here is a concept that only allows the return value types that my template class can handle: void, a number, a bool or a class:
// concept guards what types of return values we can handle template<typename R> concept Callbackable = std::is_void<R>::value || std::is_arithmetic_v<R> || std::is_class_v<R>;
The type bool is part of the arithmetic family. It is covered by the second check.
In my class, I enforce that return values comply to the concept:
template <Callbackable R, typename... Args> class Callback { using callbackfunction_t = std::function<R(Args...)>; public: // ...
That's it. A little bit of support for the consumers of your software. They get an easier experience. At no runtime costs.
And you as designer will get less support requests. The default error suggests that your template class has an issue. When using a concept, it informs the developer that they try something unsupported.
Thank you for reading.