Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Support for user defined constructors #50

Open
SpintroniK opened this issue May 6, 2024 · 7 comments
Open

Feature: Support for user defined constructors #50

SpintroniK opened this issue May 6, 2024 · 7 comments

Comments

@SpintroniK
Copy link

SpintroniK commented May 6, 2024

I've been using this library, and I think it's fantastic. Well done!
It looks like things started when this was posted: [C++20] 60 LOC constexpr get_name for members / no ifdefs / clang,gcc,msvc .
I've been playing a bit with this example and realized it's possible to get structs name even if a user defined constructor exists.
It's also possible to iterate over the members of a struct that has a user defined constructor.
So far, getting names and values of a struct's members works if a user defined constructor exists.
Of course, it only works if:

  • A default constructor exists, e.g. foo() = default;.
  • A constructor with an argument for each struct's member exists.
    Second point means:
struct Point
{
  Point() {}
  Point(int x_, int y_) : x{x_}, y{y_} {}
  int x;
  int y;
};

If I have both constructors, I can do something like:

struct Point
{
  Point() {}
  Point(int x_, int y_, int z_) : x{x_}, y{y_}, z{z_} {}
  Point(int x_, int y_) : x{x_}, y{y_}, z{0} {}
  int x;
  int y;
  int z;
};

And it works, I can iterate over all three members and get their names and values.
Of course, this means that the is_aggregate_v constraint no longer applies, and that the first two constructors need to exist, otherwise it fails to compile.

Do you think that's something that could be added to reflect to make it work on structs that have user defined constructors?

@kris-jusiak
Copy link
Contributor

Thanks @SpintroniK.

Interesting, thanks for sharing. As far I understand, this is actually deducing the constructor parameters not the members, it just happen that in this examples constructor parameter match the public members. That would break if the constructor had different types as they wouldn't match members, right? I also think that not all constructors needed to be defined, you can just try to find the longest one up to sizesof(struct) or something. For the constructor parameters, there is also a way to deduce the - https://godbolt.org/z/3faKq5x3q.

So I think constructor traits would be a great addition, though not sure about relying on the fact that constructor matches member parameters unless I'm missing something? We would need to be able to somehow guarantee constructors and members are matching? Interested to hear thoughts regarding the approach/concerns?

@SpintroniK
Copy link
Author

Very interesting, I like the constructor_traits!
Not sure why I needed a default constructor, I think it was related to counting the members of the struct.
I need to test that again, but apparently it's not strictly required, so I guess the constructor_traits is the way to go.
You're right about not matching the members: https://godbolt.org/z/hM91zzxYM.
The only thing that seem to be very important is the number of arguments of the constructor.

@kris-jusiak
Copy link
Contributor

Thanks for following-up. It's very interesting how it actually works. Will add constructor traits to reflect.

@SpintroniK
Copy link
Author

That would be very nice! I've been wanting to reflect on structs with constructors for a while.
Been playing a little bit with the code you shared.
This is what I came up with: https://godbolt.org/z/3s14PEv7K with the help of this post Counting the number of fields in an aggregate in C++20.

I didn't keep the code I wrote yesterday, but I think I had to add a default constructor when working with reflect.
At first I didn't and got a compile error, not sure why though...

@kris-jusiak
Copy link
Contributor

Just an update - https://github.com/qlibs/di supports constructor deduction and injection; there is ctor_traits which can accomplish that )by default it returns the constructor with the longest number of parameters but potentially it can be a list of all available candidates, it supports value categories too).

Example - https://godbolt.org/z/e98M6eq3q

struct Point
{
  Point() {}
  Point(int x_, int y_, int z_) : x{x_}, y{y_}, z{z_} {}
  Point(int x_, int y_) : x{x_}, y{y_}, z{0} {}
  int x;
  int y;
  int z;
};

template<class...> struct show_type;

int main() {
   show_type<di::ctor_traits<Point>::type>{}; // show_type<di::type_list<int, int, int>>
}

@SpintroniK
Copy link
Author

That's pretty cool. The constructor with the longest number of parameters seems like a good default.
Any chance to have that integrated into reflect to allow reflection on aggregates with constructors?

@kris-jusiak
Copy link
Contributor

Yeah, let's add it inot reflect

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants