I have some reflective visitor function for a struct that looks like this:
namespace ns {
struct something {
int x;
int y;
};
template <typename V>
void visit_members_adl(V&& visitor, const something& instance) {
visitor("x", instance.x);
visitor("y", instance.y);
}
} // namespace ns
I want to allow any class which has this visit_members_adl
free function defined in its same namespace can be automatically serialized to JSON, using nlohmann's JSON library. I did this:
#include "json.hpp"
// placeholder concept
template <typename T>
concept Visitable = requires(T t) {
{ t } -> std::convertible_to<ns::something>;
};
namespace nlohmann {
template <Visitable T>
struct adl_serializer<T> {
static void to_json(nlohmann::json& j, const T& t) {
auto visitor = [&j](const char* name, const auto& member) {
j[name] = member;
};
visit_members_adl(visitor, t);
}
};
} // namespace nlohmann
int main() {
ns::something s{1, 2};
if constexpr (Visitable<ns::something>) {
nlohmann::json j;
printf("Visitable = true: %s\n", j.dump().c_str());
} else {
printf("Visible = false\n");
}
return 0;
}
And it works! But obviously, it's too narrow a constraint.
Is there a way to define this Visitable
concept as a broader constraint that captures the idea "visit_members_adl(visitor, t)
is a valid expression for any visitor with the right signature"? Or am I thinking about this the wrong way from the jump?
It looks like this declaration
template <typename T>
concept Visitable = requires(T t) {
visit_members_adl([](const char*, const auto&) {}, t);
};
does what you want.