Closed RalphSteinhagen closed 2 years ago
Hey Ralph, function descriptors have a pointer
field which is valid if the function is not overloaded (is_resolved
is true). This is documented here: refl::descriptor::function_descriptor< T, N >::pointer
.
One can write a template specialisation to extract the argument types from that:
template <typename...>
struct mem_fun_types;
template <typename C, typename R, typename... Args>
struct mem_fun_types<R(C::*)(Args...)> {
using arg_types = type_list<Args...>;
using return_type = R;
};
using pointer_type = decltype(func.pointer); // -> pointer of type double (Circle::* const)()
using arg_types = typename mem_fun_types<pointer_type>::arg_types;
Note that you would need additional specialisations for the different possible cv-qualifiers on member functions (if they can be encountered in your codebase):
struct mem_fun_types<R(C::*)(Args...)>
struct mem_fun_types<R(C::*)(Args...) const>
struct mem_fun_types<R(C::*)(Args...) const volatile &>
Constructors are not really typed as member functions so there isn't anything that can be done with them AFAIK. (One cannot take a pointer to a constructor.) You could use a custom attribute on the type itself to specify information about the constructor, but it might be helpful to first understand the exact use case.
Are you aware of this example here: https://github.com/veselink1/refl-cpp/blob/master/examples/example-binding.cpp It shows how a made-up GUI system could deserialize XML strings into objects using refl-cpp.
<StackPanel orientation="horizontal"> Hello, World! </StackPanel>
It wraps the deserialized output into an std::any
(which can be downcast to a concrete type StackPanel
).
Hey Vesko, thanks for the brilliant suggestion w.r.t. the 'template specialisation' trick.
Are these helper functions something that could be potentially also useful for others using refl-cpp?
I needed just a minor modification (R(C::*)(Args...)
-> R(C::* const)(Args...)
) and your works out-of-the-box:
template <typename C, typename R, typename... Args>
struct mem_fun_types<R (C::*const)(Args...)> {
using arg_types = std::tuple<Args...>;
using return_type = R;
};
For posterior -- in case, someone else stumbles across this question -- I made a more fleshed-out example on compiler-explorer to illustrate your proposal. For my use case this will do.
Will need to think about the constructors a bit more but it should also be solvable one way or the other.
Are you aware of this example here: https://github.com/veselink1/refl-cpp/blob/master/examples/example-binding.cpp
Yes, this is a similar motivating example. I mentioned 'GUI' because this is a use case that is more accessible for the general audience. My use-case is slightly different and non-UI related (i.e. dynamic parsing, modification, reinitialisation of GNU Radio flow graphs -> here).
Thanks again for your quick help and suggestions! :+1:
Will close this since my initial question has been answered.
Sorry about the typo. Thanks for linking a working example.
Are these helper functions something that could be potentially also useful for others using refl-cpp?
They could be useful, but I've refrained from adding them because they are available in other libraries in common use (e.g. function_traits
in boost/type_traits).
FWIW, I don't think it is possible to deduce the argument types of constructors. This works for (member) functions because they are not overloaded and the compiler can deduce the type of the pointer to use in the expression (&Circle::getRadius)
, but if you think about it, constructors are always overloaded (because of implicitly generated constructors):
Circle::Circle(double)
Circle::Circle(const Circle&)
Circle::Circle(Circle&&)
Sorry about the typo.
No worries, this just seems to confirm that you are human. :smile:
They could be useful, but I've refrained from adding them because [..] boost/type_traits.
I fully understand. Keeping the API envelope small eases maintenance.
FWIW, I don't think it is possible to deduce the argument types of constructors.
Yes and this isn't critical, because having one default constructor and one constructor with explicit custom attributes declared should work for the targeted application where these classes usually have only a limited number of constructors but potentially many member functions. The application is still in an explorative state but just wanted to check before investing too much in this with an MVP.
In any case, thanks a lot for your feedback!
We are using refl-cpp primarily for reflections on member fields which works perfectly fine for our use case.
We may have a new application now, where I was wondering whether this could be also extended also to member functions, notably to retrieve their parameter and return type signatures during compile time.
For example, having the following demo class definition:
How would one go about retrieving the
setRadius(double)
calling parameter (double) and return value (void)? Or, more generally, the number of calling parameters ofgetFunc(...)
which would bestd::tuple<int,double,std::string>
and its return typestd::string
?There is an example that illustrates how to get the function pointer and how to call 'std::invoke(..)` but this seems to implicitly assume the calling parameter and return types:
Also more generally, how would one register, find and call the different class constructors and parameter signatures?
This (potentially new) functionality has applications in GUIs and other areas where the compile/run-time definitions are driven by configuration files.
Any help, suggestions, or feedback would be welcome. Thanks in advance.