Closed Pennywise007 closed 4 months ago
Unfortunately, there is no easy way to fix this. However, you can use rfl::Flatten
instead.
https://github.com/getml/reflect-cpp/blob/main/docs/flatten_structs.md
So you could rewrite your code as follows:
struct Homer {
rfl::Flatten<Characteristics> characteristics;
rfl::Field<"work", std::string> company_name;
};
const Homer homer = {Characteristics{"Homer", "Simpson"}, "Station"};
This would result in the following JSON string and that JSON string could also be read into your struct:
{"name":"Homer","surname":"Simpson","work":"Station"}
The problem is with inheritance and structured bindings is that structured bindings only work if only one of the structs actually contain fields.
Every non-static data member of E must be a direct member of E or the same base class of E, and must be well-formed in the context of the structured binding when named as e.name. E may not have an anonymous union member. The number of identifiers must equal the number of non-static data members. (https://en.cppreference.com/w/cpp/language/structured_binding)
So, in theory, your first example might work, but it kind of defies the point of using inheritance in the context of our library. The second example could not work. You would have to use rfl::Flatten
.
I do not think we can support inheritance until there is native support for reflection in C++ and we wouldn't have to use structured bindings as a workaround.
But I will leave the issue open in the event somebody can come up with something. But don't hold your breath.
@Pennywise007 maybe inheritance is useful, but it's not the best invention. Do not inherit an implementation, but do inherit (implement) an interface.
In this case, you will end up with:
struct Homer{
Characteristics characteristics;
rfl::Field<"work", std::string> company_name;
};
const Homer homer = {{"Homer", "Simpson"}, "Station"};
//or
const Homer homer = {Characteristics{"Homer", "Simpson"}, "Station"};
[[maybe_unused]] auto& [f1, f2] = homer;
So you have type safety here, and easiness of restructuring the properties, since they are wrapped with at least {}
.
It can support these aggregate initializable classes (which have empty bases - or have only one non-empty base class). Just use ubiq-base aggregate-hack to recognize inheritance, see here.
@liuzicheng1987 Would it be possible to (better) support inheritance in cases where structured bindings are already possible? Consider this example:
struct S {
int x;
};
struct T : S {};
T test { 2 };
auto [ xx ] = test;
for (const auto& fld : rfl::fields<T>()) {
std::cout<<fld.name()<<std::endl;
}
Here, the (somewhat unexpected) output is S::x
as the only member ends up being disambuigated by prefixing it with S::
. The use-case for those constructs is to have a aggregate-initializable base-class (which hence cannot supply advanced constructors) and supply constructors in a derived class (without data-members).
Here, the (somewhat unexpected) output is
S::x
I see this only happens with clang. gcc produces the expected x
. I don't know about other compilers. I am having a look at the source-code.
As I see you use structured binding for getting a fields list, but I guess it doesn't work with inheritance. For example:
Is a correct code and should work with structured binding, but I can't compile it with the
write_and_read
function call:rfl/internal/to_ptr_tuple.hpp(44): error C3448: the number of identifiers must match the number of array elements or members in a structured binding declaration
At the same time, this code doesn't work with structured binding from the box:
Do you have any plans to fix it? Inheritane is not the best invention but it is useful.