I want to create a concept Handler
that will check that implementers are able to accept any types that satisfy the concept Update
- without actually having to instantiate the concept with every single possible update object that I have. In my head, it would look something like this:
template <typename T>
concept Update = requires (T update) {
{ update.get_value(); } -> std::same_as<std::string>;
...
};
template <typename T>
concept Handler = requires (T handler, Update update) {
handler.on_update(update);
};
However this doesn't actually work unless I add an additional parameter to the Handler
concept and pass a specific instance of the Update
concept. Is there any way to achieve what I'm trying to do? I have considered creating a mock FakeUpdate
object that implements the Update
concept (which is really what I'm hoping the compiler will do), but that feels a little ugly.
The following example shows a bit of a hacky way to achieve what you want.
We simply define the UpdateableDummy
struct, and pass it to our Handleable
concept. It would be nice if the Handleable
concept could deduce the type passed to on_update
from the template of the Handleable
class, but this would fail for classes that are indeed Handleable
but not templates (see non_templateHandleableExample
). So perhaps in a future language standard, the type of the call to on_update
in the Handleable
concept, could be deduced from the class being checked for Handleable
lity
It should also be noted, that template <Updateable T>
in the declaration of HandleableExample
requires that T
be Updateable
.
// Example program
#include <iostream>
#include <string>
#include <concepts>
template <typename T>
concept Updateable = requires (T update) {
{ update.get_value() } -> std::same_as<std::string>;
};
// Updateable Dummy Type
struct UpdateableDummy {
std::string get_value(void) {
return "dummyValue";
}
};
template <typename U, typename V>
concept Handleable = requires (U handler, V updateableInstance) {
handler.on_update(updateableInstance);
};
// Updateable Example Type
struct UpdateableExample {
std::string get_value(void) {
return "exampleValue";
}
};
// Not Updateable Example Type
struct NotUpdateableExample {
void print_value(void) {
std::cout << "dummyValue\n";
}
};
// Handleable Example Type
template <Updateable T>
struct HandleableExample {
void on_update(T t) {
t.get_value();
}
};
// Not Handleable Example Type
template <Updateable T>
struct NotHandleableExample {
void when_update(T t) {
t.get_value();
}
};
// Handleable Struct that doesn't use templates
struct non_templateHandleableExample {
void on_update(UpdateableExample updateableInstance) {
updateableInstance.get_value();
}
};
// Works With Both UpdateableDummy and UpdateableExample Type
static_assert(Handleable<HandleableExample<UpdateableDummy>,UpdateableDummy>);
static_assert(Handleable<HandleableExample<UpdateableExample>,UpdateableExample>);
// Works with non_templateHandleableExample
static_assert(Handleable<non_templateHandleableExample,UpdateableExample>);
// Fails For not updateable inner-type
static_assert(Handleable<HandleableExample<NotUpdateableExample>,NotUpdateableExample>);
// Fails For not handleable outer-type
static_assert(Handleable<NotHandleableExample<UpdateableDummy>,UpdateableDummy>);
// Fails For not handleable outer-type AND not updateable inner-type
static_assert(Handleable<NotHandleableExample<NotUpdateableExample>,NotUpdateableExample>);
int main()
{
// Comment out intentionally failing static assertions to test the auto type keyword example
Handleable<UpdateableExample> auto handleableInstance = HandleableExample<UpdateableExample>();
}