I'm working on a class representation utility that would work in a similar way to Java's Class class. That is, a mechanism that would emulate class reflection.
#include <map>
#include <stdexcept>
#include <string>
template<typename Class>
struct class_repr {
std::map<std::string, uintptr_t> fields;
std::map<std::string, void* (Class::*)(...)> methods;
void declare_field(const std::string& name, void* pointer) {
fields[name] = reinterpret_cast<uintptr_t>(pointer);
}
template<typename R, typename ...Params>
void declare_instance_method(const std::string& name, R (Class::* pointer)(Params...)) {
methods[name] = (void* (Class::*)(...)) pointer;
}
template<typename Tp>
Tp& get_field(void* object, const std::string& name) {
if (fields.count(name) == 0) throw std::invalid_argument("Field " + name + " not declared in the class descriptor");
return *reinterpret_cast<Tp*>(uintptr_t(object) + fields.at(name));
}
template<typename R, typename ...Params>
requires std::is_same_v<R, void>
void invoke_instance_method(void* object, const std::string& name, Params&& ... params) {
if (methods.count(name) == 0) throw std::invalid_argument("Method " + name + " not declared in the class descriptor");
(reinterpret_cast<Class*>(object)->*methods.at(name))(std::forward<Params>(params)...);
}
template<typename R, typename ...Params>
requires (not std::is_same_v<R, void>)
R invoke_instance_method(void* object, const std::string& name, Params&& ... params) {
if (methods.count(name) == 0) throw std::invalid_argument("Method " + name + " not declared in the class descriptor");
return *static_cast<R*>((reinterpret_cast<Class*>(object)->*methods.at(name))(std::forward<Params>(params)...));
}
};
And below is the class I'm testing it with:
#include <iostream>
class cat {
std::string name, color;
[[nodiscard]] const std::string& get_name() {
return name;
}
[[nodiscard]] const std::string& get_color() {
return color;
}
void say(std::string&& what) {
std::cout << "[" << name << "]: " << what << std::endl;
}
void meow() {
say("meow");
}
void say_color() {
say("my fur is " + color);
}
public:
cat(std::string name, std::string color) : name(std::move(name)), color(std::move(color)) {}
static class_repr<cat> get_representation() {
class_repr<cat> descriptor;
descriptor.declare_field("name", &(static_cast<cat*>(nullptr)->name));
descriptor.declare_field("color", &(static_cast<cat*>(nullptr)->color));
descriptor.declare_instance_method("get_name", &cat::get_name);
descriptor.declare_instance_method("get_color", &cat::get_color);
descriptor.declare_instance_method("say", &cat::say);
descriptor.declare_instance_method("meow", &cat::meow);
descriptor.declare_instance_method("say_color", &cat::say_color);
return descriptor;
}
};
This code works fine:
int main() {
cat kitty("marble", "white");
class_repr cat_class = cat::get_representation();
cat_class.get_field<std::string>(&kitty, "name") = "skittle";
cat_class.get_field<std::string>(&kitty, "color") = "gray";
cat_class.invoke_instance_method<void>(&kitty, "meow");
cat_class.invoke_instance_method<void>(&kitty, "say_color");
std::cout << cat_class.invoke_instance_method<std::string>(&kitty, "get_name") << "'s color is indeed "
<< cat_class.invoke_instance_method<std::string>(&kitty, "get_color") << std::endl;
return 0;
}
But when I try to call the say function, the code doesn't compile because non-primitive type objects cannot be passed through variadic method:
cat_class.invoke_instance_method<void, std::string&&>(&kitty, "say", "purr"); // error
Is there any way around making this work as intended (so that it calls an equivalent of kitty.say("purr"))?
warning: cast between incompatible pointer to member types from 'const std::__cxx11::basic_string<char>& (cat::*)()' to 'void* (cat::*)(...)' [-Wcast-function-type]methods[name] = (void* (Class::*)(...))pointer;std::invoke?std::functiondoesn't help much, probably I was thinking unify pointer to member and pointer to member function that time. (&(static_cast<cat*>(nullptr)->name)) orstd::function<void*(T*)>for parameterless function.