C++ Standard: 20 or 23
test_property.cpp
#include <string>
#include <tuple>
#include "property.h"
class object {
public:
int index() const {
return this->_idx;
}
void set_index(int value) {
this->_idx = value;
}
const std::string& name() const {
return this->_name;
}
void set_name(const std::string& value) {
this->_name = value;
}
private:
bool _flag{};
int _idx{};
std::string _name{};
constexpr static auto properties = std::make_tuple(
property("name", &object::name, &object::set_name),
property("index", &object::index, &object::set_index),
property("flag", &object::_flag)
);
};
int main(int argc, char** argv) {
object obj;
return 0;
}
property.h v1 w/ enable_if
#include <type_traits>
template <typename class_type, typename data_type>
class property {
public:
template <typename = std::enable_if_t<!std::is_reference_v<data_type>>>
inline constexpr property(const char* name, data_type class_type::*property) {
}
inline constexpr property(const char* name, data_type (class_type::*getter)() const, void (class_type::*setter)(data_type)) {
}
};
property.h w/ concepts
#include <type_traits>
template <typename class_type, typename data_type>
class property {
public:
inline constexpr property(const char* name, data_type class_type::*property) requires(!std::is_reference_v<data_type>){
}
inline constexpr property(const char* name, data_type (class_type::*getter)() const, void (class_type::*setter)(data_type)) {
}
};
I am trying to keep consideration of the signature of the first constructor from being evaluated if data_type
is any reference type since a pointer to a reference member is illegal (in the example above, name
/set_name
trigger this condition). I would have expected, being constexpr, that the first constructor wouldn't even be evaluated for the name
/set_name
case yet it is. So I tried using enable_if
and a requires
statement but neither prevents the signature itself from being evaluated (which in hindsight makes sense since it has to evaluate the signature to determine class_type
and data_type
).
Is there any way to achieve what I am attempting to achieve here (namely being able to accept a class member and create a lambda for the getter/setter or accept a getter/setter pair in a constexpr context within the same object)?
data_type
is in the parameter list, so it will always be instantiated.
You can template the data_type
template parameter for the constructor and provide deduction guides yourself
template <typename class_type, typename data_type>
class property {
public:
template<class dummy = data_type>
constexpr property(const char* name, dummy class_type::*property) {
}
template<class dummy = data_type>
constexpr property(const char* name, dummy (class_type::*getter)() const, void (class_type::*setter)(dummy)) {
}
};
template <typename class_type, typename data_type>
property(const char*, data_type class_type::*) -> property<class_type, data_type>;
template <typename class_type, typename data_type>
property(const char*, data_type (class_type::*)() const, void (class_type::*)(data_type))
-> property<class_type, data_type>;