Can I get the Owning Object of a Member Function Template Parameter?
Asked Answered
F

2

12

Given a object:

struct foo {
    void func();
};

Now given the templatized function declaration:

template<typename T, T F>
void bar();

So bar will be taking in a member function like so:

bar<decltype(&foo::func), &foo::func>()

In the body of bar I want to recover the type foo from T. Can I do that? I want to be able to do something like this:

get_obj<T> myfoo;

(myfoo.*F)();

I know that get_obj isn't a thing, but would there be a way to write it?

Filemon answered 13/9, 2018 at 15:34 Comment(3)
I figured I should mention that I do know that I can simplify bar to template<auto F> void bar() This just seemed simpler to write it this way.Filemon
"something like this" do you mean to do myfoo.*F instead of foo.*F? And it appears you want to find the object type that a given member function is for, not the actual object?Cadal
@Yakk-AdamNevraumont Yes you're right. I'm failing at freehanding code today :(Filemon
C
14
template<class T>
struct get_memfun_class;
template<class R, class T, class...Args>
struct get_memfun_class<R(T::*)(Args...)> {
  using type=T;
};
template<class T>
using get_memfun_class_t=typename get_memfun_class<T>::type;


template<auto M>
using class_of_memfun = get_memfun_class_t< decltype(M) >;

class_of_memfun<F> is then the class of the memberfunction F.

To handle const/volatile/etc you end up having to do a bunch of versions. This is annoying. Here is an example of it:

template<class T>
struct get_memfun_class;
#define GET_MEMFUN_CLASS(...) \
template<class R, class T, class...Args> \
struct get_memfun_class<R(T::*)(Args...) __VA_ARGS__> { \
  using type=T; \
}

possibly you want:

template<class R, class T, class...Args> \
struct get_memfun_class<R(T::*)(Args...) __VA_ARGS__> { \
  using type=T __VA_ARGS__; \
}

is the type of the class of a pointer to a const memfun a const class or not?

Once you have chosen, you need to write 24 uses of the above macro:

GET_MEMFUN_CLASS();
GET_MEMFUN_CLASS(const);
GET_MEMFUN_CLASS(volatile);
GET_MEMFUN_CLASS(const volatile);
GET_MEMFUN_CLASS(&);
GET_MEMFUN_CLASS(const&);
GET_MEMFUN_CLASS(volatile&);
GET_MEMFUN_CLASS(const volatile&);
GET_MEMFUN_CLASS(&&);
GET_MEMFUN_CLASS(const&&);
GET_MEMFUN_CLASS(volatile&&);
GET_MEMFUN_CLASS(const volatile&&);
GET_MEMFUN_CLASS(noexcept);
GET_MEMFUN_CLASS(const noexcept);
GET_MEMFUN_CLASS(volatile noexcept);
GET_MEMFUN_CLASS(const volatile noexcept);
GET_MEMFUN_CLASS(& noexcept);
GET_MEMFUN_CLASS(const& noexcept);
GET_MEMFUN_CLASS(volatile& noexcept);
GET_MEMFUN_CLASS(const volatile& noexcept);
GET_MEMFUN_CLASS(&& noexcept);
GET_MEMFUN_CLASS(const&& noexcept);
GET_MEMFUN_CLASS(volatile&& noexcept);
GET_MEMFUN_CLASS(const volatile&& noexcept);
#undef GET_MEMFUN_CLASS

template<class T>
using get_memfun_class_t=typename get_memfun_class<T>::type;

I am unaware of a way to avoid doing all 24 of these specializations for full coverage. If you think this is dumb, you are right; please feel free to express your annoyance by proposing a fix to the C++ standard committee.

If you are doing something like this for more than one trait, you can write the "strip lvalue, rvalue, noexcept and cv qualifiers" off part at one spot and pass them down in pieces.

Live example.

Cadal answered 13/9, 2018 at 16:6 Comment(11)
Ah, even the *_t nice touch. So in my example I could call this like: get_memfun_class_t<T> myfooFilemon
OK just for my own understanding, I believe the second get_memfun_class is a specialization, right? Yet it takes more template arguments? I was trying to figure out how to do this by defaulting, but it looks like we can specify a different number of template arguments for a specialization?Filemon
@JonathanMee as I understood, the crucial point is get_memfun_class<R(T::*)(Args...)> on the specialization, so it actually still takes one parameterEnisle
But the template<class R, class T, class... Args> aren't those 3 arguments? I mean I guess they can't be because objects must be fully specified on declaration, and either way this only takes one argument. But if not arguments what are R, T, and Args? Maybe this should be made into a second question...Filemon
OK, I've tried to clarify this question here: https://mcmap.net/q/1008050/-when-specializing-a-class-how-can-i-take-a-different-number-of-template-parameters/2642059Filemon
this is a good solution, except for the fact that all that effort class R, class T, class...Args will fail when an overloaded member function is available. That's not OP's case though.Soutor
@Soutor with overloads member function pointers are a bit tricky anyhow, thats not really a problem of this solution. Btw you can always pick the overload you want by casting it to a memfunpointer with the right signatureEnisle
ah finally someone upvoted this but not mine. didnt feel right beforeEnisle
@Soutor Overloaded member functions are not objects and have no type. ;)Cadal
Beware that this fails for const, volatile or static member functions.Spigot
@FrançoisAndrieux Yes; and I just added the really annoying boilerplate to fix that. There is a bit of a question; is the class of a const member function a const type of the class? What about r/lvalue qualified member functions?Cadal
E
7

If you restrict to void(T::mem_fun)():

#include <iostream>
struct foo {
    void func(){ std::cout << "foo"; }
};

template <typename T> struct get_type;
template <typename T> struct get_type<void(T::*)()> {
    using type = T;
};
template <typename T> using get_type_t = typename get_type<T>::type;


template<typename T, T F> void bar(){
    get_type_t<T> myfoo;
    (myfoo.*F)();
}

int main () {
    bar<decltype(&foo::func), &foo::func>();
}
Enisle answered 13/9, 2018 at 15:50 Comment(5)
Can we extend this with something like template<typename T, typename R, typename Ts...> struct get_type<R(T::*)(Ts...)> { using type = T; };Filemon
@JonathanMee I took the (myfoo.*F)(); as given. With arbitrary return and params i guess it is possible just a bit more complicated ;)Enisle
Beware that this fails for const, volatile or static member functions.Spigot
@FrançoisAndrieux static is kinda ovious, the other cases arent that clear to me, will have to do some research. Thanks for the commentEnisle
@user463035818 You can fix the cv problem by adding specializations like template <typename T> struct get_type<void(T::*)() const>, etc. There may be a clever solution with a std::remove_cv_t but I don't see it.Spigot

© 2022 - 2024 — McMap. All rights reserved.