I'm writing simple code for automatic reflection of struct. in short each struct have static tuple with name and member pointer and code iterate over it using std::apply and do some stuff.
struct TestRecord{
int id;
long counter;
};
template<>
struct RepositoryHelperID<TestRecord>
{
static constexpr auto members = [] {
return std::make_tuple(
std::make_tuple("id"sv, &TestRecord::id),
std::make_tuple("counter"sv, &TestRecord::counter));
}();
};
it is working but i have some helper function (getDBType) for each type of member needed to whole code to work. to minimize compilation error size in case of missing this helper function i decided to create concept which should:
but i'm unable to make it work. if all function exist code compiles and it work. when function is missing i expect that concept is not fulfilled but instead i have compilation error about missing function. i assumed if function is missing then require is not meet and concept fails, but as far i deduced template function needs to be evaluated to then check require statement. i try multiple combination of lambda function in std::apply but none worked.
i tried :
below extracted full example of idea. and compiler explorer link https://godbolt.org/z/zss8aTM13
the question is if it is possible to make concept using tuples and std::apply.
#include <array>
#include <iostream>
#include <tuple>
#include <string_view>
using namespace std::string_view_literals;
template <std::same_as<int> T>
std::string getDBType(){
return "int";
}
template <std::same_as<double> T>
std::string getDBType(){
return "double";
}
template<typename T>
struct RepositoryHelperID
{
};
struct TestRecord{
int id;
long counter; // change to double to compile
};
template<>
struct RepositoryHelperID<TestRecord>
{
static constexpr auto members = [] {
return std::make_tuple(
std::make_tuple("id"sv, &TestRecord::id),
std::make_tuple("counter"sv, &TestRecord::counter));
}();
};
template <typename T>
concept HaveGetType =
requires {
{
getDBType<T>()
};
};
template <typename T>
concept RepositoryHelperConcept =
requires(T) {
{
RepositoryHelperID<T>::members
};
{
// std::remove_reference_t<decltype(std::declval<T>().*std::get<1>(args))> == int , double etc. so check if eg. getDBType<int>() exists
std::apply([](auto&&... args) {
((getDBType<std::remove_reference_t<decltype(std::declval<T>().*std::get<1>(args))>>()), ...);
},
RepositoryHelperID<T>::members)
};
};
int main(){
///static_assert(!RepositoryHelperConcept<TestRecord>);
return !RepositoryHelperConcept<TestRecord>;
}
std::apply
is an unconstrained function, so the requires
-clause will always trigger hard errors when the lambda passed in is ill-formed.
However, you can still use std::apply
to check whether the return type of each member pointer after being invoked by the object satisfies HaveGetType
template <typename T>
concept HaveGetType = requires {
getDBType<T>();
};
template <typename T>
concept RepositoryHelperConcept = requires {
RepositoryHelperID<T>::members;
requires std::apply([](auto&&... args) {
return (
HaveGetType<
std::remove_reference_t<decltype(std::declval<T>().*std::get<1>(args))>
> && ...);
}, RepositoryHelperID<T>::members);
};