lundi 14 septembre 2020

С++ invoke member function of unknown class

So I'm trying to create a small compile-time reflection lib & I've got it to work with primitive data types and struct/class. The code is also here:

reflection.h

struct RTypeDescriptor
{
    const char* Name;
    size_t Size;
    
    RTypeDescriptor(const char* Name, size_t Size) : Name(Name), Size(Size)
    {
    }
    
    virtual ~RTypeDescriptor() = default;

    virtual std::string GetPublicName() const
    {
        return Name;
    }

    virtual void Print(const void* Obj) const = 0;
};
    
struct RStructDescriptor : RTypeDescriptor
{
    struct Function
    {
        struct Param
        {
            const char* Name;
            RTypeDescriptor* Type;
        };

        // NULL means return type is void
        RTypeDescriptor* ReturnType;
        const char* Name;
        std::vector<Param> Params;
    };

    struct Member
    {
        const char* Name;
        size_t Offset;
        RTypeDescriptor* Type;
    };

    std::vector<Member> Members;
    std::vector<Function> Functions;

    RStructDescriptor(void(*Init)(RStructDescriptor*)) : RTypeDescriptor{nullptr, 0}
    {
        Init(this);
    }

    void Print(const void* Obj) const override
    {
        std::cout << Name << std::endl << "{" << std::endl;
        for (const Function& f : Functions)
        {
            if (!f.ReturnType)
                std::cout << "void ";
            else
                std::cout << f.ReturnType->GetPublicName() << " ";
            std::cout << Name << "(";
            for (const Function::Param& p : f.Params)
            {
                std::cout << p.Type->GetPublicName() << " " << p.Name << ", ";
            }
            std::cout << ");" << std::endl;
        }
    
        for (const Member& m : Members)
        {
            std::cout << "    " << m.Name << " = ";
            m.Type->Print((char*)Obj + m.Offset);
            std::cout << std::endl;
        }
        std::cout << "}" << std::endl;
    }
};

template<typename T>
RTypeDescriptor* GetPrimitiveDescriptor();

struct RDefaultResolver
{
    template <typename T> static char Func(decltype(&T::Reflection));
    template <typename T> static int Func(...);
    template <typename T>
    struct IsReflected {
        enum { value = (sizeof(Func<T>(nullptr)) == sizeof(char)) };
    };

    // This version is called if T has a static member named "Reflection":
    template <typename T, typename std::enable_if<IsReflected<T>::value, int>::type = 0>
    static RTypeDescriptor* Get() {
        return &T::Reflection;
    }

    // This version is called otherwise:
    template <typename T, typename std::enable_if<!IsReflected<T>::value, int>::type = 0>
    static RTypeDescriptor* Get() {
        return GetPrimitiveDescriptor<T>();
    }
};

template<typename T>
struct RTypeResolver
{
    static RTypeDescriptor* Get()
    {
        return RDefaultResolver::Get<T>();
    }
};

#define REFLECT() \
friend struct RDefaultResolver; \
static RStructDescriptor Reflection; \
static void InitReflection(RStructDescriptor*); 

#define BEGIN_STRUCT_REFLECTION(Typename) \
RStructDescriptor Typename::Reflection{ Typename::InitReflection}; \
void Typename::InitReflection(RStructDescriptor* Desc) \
{ \
    using T = Typename; \
    Desc->Name = #Typename; \
    Desc->Size = sizeof(T);

#define BEGIN_STRUCT_MEMBER_LIST() \
    Desc->Members = \
    {

#define REFLECT_STRUCT_MEMBER(Name) \
        {#Name, offsetof(T, Name), RTypeResolver<decltype(T::Name)>::Get()},

#define END_STRUCT_MEMBER_LIST() \
    };

#define BEGIN_STRUCT_FUNCTION_LIST() \
    Desc->Functions = \
    {

#define BEGIN_STRUCT_FUNCTION_DEFINITION(Ret, Name) \
        { RTypeResolver<Ret>::Get(), #Name, \
            {

#define REFLECT_FUNCTION_PARAM(Type, Name) \
                {#Name, RTypeResolver<Type>::Get()},

#define END_STRUCT_FUNCTION_DEFINITION() \
            } \
        },

#define END_STRUCT_FUNCTION_LIST() \
    };

#define END_STRUCT_REFLECTION() \
}

-----------------------------------------------------------------

reflection.cpp

#include "reflection.h"

struct RIntDescriptor : RTypeDescriptor
{
    RIntDescriptor() : RTypeDescriptor("int", sizeof(int)) {}

    void Print(const void* Obj) const override
    {
        std::cout << "int{" << *(const int*)Obj << "}";
    }
};

struct RFloatDescriptor : RTypeDescriptor
{
    RFloatDescriptor() : RTypeDescriptor("float", sizeof(float)) {}

    void Print(const void* Obj) const override
    {
        std::cout << "float{" << *(const float*)Obj << "}";
    }
};

template<>
RTypeDescriptor* GetPrimitiveDescriptor<int>()
{
    static RIntDescriptor Descriptor;
    return &Descriptor;
}

template<>
RTypeDescriptor* GetPrimitiveDescriptor<float>()
{
    static RFloatDescriptor Descriptor;
    return &Descriptor;
}

Example usage:

#include "reflection.h"

struct Vec3f
{
    float X, Y, Z;

    REFLECT()
};

struct Test
{
    int Index;
    Vec3f Location;

    float Scale(float Num, int Scale)
    {
        float A = Num * Scale;
        return A;
    }

    REFLECT()
};

BEGIN_STRUCT_REFLECTION(Vec3f)
BEGIN_STRUCT_MEMBER_LIST()
REFLECT_STRUCT_MEMBER(X)
REFLECT_STRUCT_MEMBER(Y)
REFLECT_STRUCT_MEMBER(Z)
END_STRUCT_MEMBER_LIST()
END_STRUCT_REFLECTION()

BEGIN_STRUCT_REFLECTION(Test)
BEGIN_STRUCT_MEMBER_LIST()
REFLECT_STRUCT_MEMBER(Index)
REFLECT_STRUCT_MEMBER(Location)
END_STRUCT_MEMBER_LIST()
BEGIN_STRUCT_FUNCTION_LIST()
BEGIN_STRUCT_FUNCTION_DEFINITION(float, Scale)
REFLECT_FUNCTION_PARAM(float, Num)
REFLECT_FUNCTION_PARAM(int, Scale)
END_STRUCT_FUNCTION_DEFINITION()
END_STRUCT_FUNCTION_LIST()
END_STRUCT_REFLECTION()

int main()
{
    Test t{ 1, {1.0f, 2.0f, 3.0f} };
    RTypeDescriptor* Des = RTypeResolver<Test>::Get();
    Des->Print(&t);
    return 0;
}

And the problem now is that I have no clue how can I add the ability to invoke reflected functions on custom objects, i.e. I want to enumerate all member functions of some class or struct and call one that matches the name I need. Please don't say something like "just use an existing library and don't reinvent the wheel". I'm doing this in learning purposes and want to implement it myself as much as possible.





Aucun commentaire:

Enregistrer un commentaire