veselink1 / refl-cpp

Static reflection for C++17 (compile-time enumeration, attributes, proxies, overloads, template functions, metaprogramming).
https://veselink1.github.io/refl-cpp/md__introduction.html
MIT License
1.05k stars 76 forks source link

Usage help #40

Closed kitts82 closed 3 years ago

kitts82 commented 3 years ago

I just came across this library and I must admit that it's bit too complex for an average user. Nevertheless, i appreciate the immense amount of work into it. I have a particular requirement that brought me here and i would like to know how to achieve them if that is possible.

1) Is there an equivalent of offsetof(struct, member) and sizeof(member)? My use case requires me to read and write the whole structure or portions of it as a bytestream. This can be achieved with something like... memcpy(buffer, &myStructAsByteStream[offsetof(mystruct, member)], bytesToSend)

2)Is there a way to get the index of a member of the struct? 3) Is there a way to get the member from index? This is the inverse of the previous.

veselink1 commented 3 years ago

Hello! I agree that the documentation can be improved. Here is how these operations would translate to refl-cpp. If more flexibility is needed, you can always map the compile-time member metadata to whatever runtime structure is useful for your usecase.

// Get member descriptor by index
constexpr refl::trait::get_t<0, refl::member_list<A>> member_first{};
std::cout << "A.members[0]=" << member_first.name.str() << std::endl;

// Get member by name (name must be a constant expression)
constexpr auto x_member = find_one(refl::member_list<A>{}, [](auto m) { return m.name == "x"; });
std::cout << "x_member.name=" << x_member.name.str() << std::endl;

// Get index of member
constexpr auto index_of_x = refl::trait::index_of_v<refl::trait::remove_qualifiers_t<decltype(x_member)>, refl::member_list<A>>;
std::cout << "index_of_x=" << index_of_x << std::endl;

You could cast the member pointer member.pointer to an offset if you need to. If you need more flexibility at runtime, you could use refl::util::map_to_array to convert the field_descriptors to your own custom structure like so:

constexpr auto members = refl::util::map_to_array<MyMemberInfo>(refl::member_list<A>{}, [](auto m) {
    return MyMemberInfo(m); 
});

The following resources might be helpful:

You can also refer to the Introduction.md. Also take a look at the examples at the bottom of that page.

kitts82 commented 3 years ago

I have gotten your code to work but still not very clear how i can use it in code especially in non constexpr code. Interestingly, I reached this code when searching for code capable of constexpr compile time operation. Unfortunately, it is a bit too complex for quick comprehension.

I have attached a sample code of what I would like to achieve. It would be much appreciated if you can convert the code to real working code.

#include "refl.hpp"
#include <iostream>
#include <cstdint>

struct Base
{
    uint16_t a = 0;
    uint16_t b = 1;
    uint16_t c = 2;
    uint16_t d = 3;
}

struct DerivedA : public Base
{
    uint16_t e = 4;
    uint16_t f = 5;
    uint16_t g = 6;
}

struct DerivedB : public Base
{
    uint32_t g = 7;
    uint32_t f = 8;
    uint32_t e = 9;
}

void printInfo(Base *obj)
{
    std::cout << "Initial values" << std::endl;
    std::cout << "a = " << refl::value(obj, a) << std::endl;
    std::cout << "e is a member variable at position " << refl::indexof(obj, e) << " with the value " << refl::value(obj, e) << " having an address offset of " << refl::offsetof(obj, e) << std::endl;
}

void main()
{
    DerivedA A;
    DerivedB B;

    printInfo(&A);

    //Set the value of a to 50
    refl::setValue(B, a, 50);

    printInfo(&B);
}

/*Expected output*/
Initial values
a = 0
d is a member variable at position 4 with the value 4 having an address offset of 8;
Initial values
a = 50
d is a member variable at position 6 with the value 9 having an address offset of 16;
kitts82 commented 3 years ago
// Get member descriptor by index
constexpr refl::trait::get_t<0, refl::member_list<A>> member_first{};
std::cout << "A.members[0]=" << member_first.name.str() << std::endl;

by using the idex as a template argument for get_t, It is not possible to get the member by a variable index. Is it possible to do something like refl::reflect<MyStruct>().members[indexVariable].name

veselink1 commented 3 years ago

No, it is not. Copied from above: If you need more flexibility at runtime, you could use refl::util::map_to_array to convert the field_descriptors to your own custom structure like so:

constexpr auto members = refl::util::map_to_array<MyMemberInfo>(refl::member_list<A>{}, [](auto m) {
    return MyMemberInfo(m); 
});

Example MyMemberInfo:

struct MyMemberInfo {
    std::string name;

    template <typename Member>
    MyMemberInfo(Member m) 
        : name(m.name.str())
    {
    }
};

Then you would be able to access any property of interest like so members[idx].name at runtime.

kitts82 commented 3 years ago

You could cast the member pointer member.pointer to an offset if you need to.

Could you expand on this? How can a member pointer be converted to offset? Did you mean; cast to size_t and then subtract the value of the pointer to the object that contains the member?

veselink1 commented 3 years ago

See the possible implementations in the abstract http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0908r0.html

veselink1 commented 3 years ago

Closing due to inactivity.