C++ typedef member function signature syntax
Asked Answered
Z

5

38

I want to declare type definition for a member function signature. Global function typedefs look like this:

typedef int (function_signature)(int, int);
typedef int (*function_pointer) (int, int);

But I'm not able to the same thing for a member function:

typedef int (foo::memberf_signature)(int, int);   // memberf_pointer is not a member of foo
typedef int (foo::*memberf_pointer)(int, int);

It sounds logically to me, because foo:: is the syntax to access a member in the class foo.

How can I typedef just the signature?

Zwieback answered 28/1, 2011 at 19:31 Comment(5)
Just out of curiosity, why are you trying to do this?Dustan
I'm confused, is the last typedef not what you want?Galloon
That seems inhomogeneous to me. It's possible to typedef a function declared at global-scope, but it isn't possible to typedef a method. And yes, I'm differentiating between a signature and a function pointer type.Zwieback
yeah C++ syntax should allow int (foo::&memberf_reference)(int, int )Aureliaaurelian
@Galloon 10 years late, I know (there might be other late readers, though…) – but I see clearly the intention: typedef int (function_signature)(int, int); allows to write later on function_signature* thePointer – not hiding the pointer nature of the variable as the pointer typedef would do, and apparently the same should be done for member function pointers ;)Evocative
S
31

For questions regarding the awkward function pointer syntax, I personally use a cheat-sheet: The Function Pointers Tutorial (downloadable here, thanks to Vector for pointing it out).

The signature of a member function, however, is a bit different from the signature of a regular function, as you experienced.

As you probably know, a member function has a hidden parameter, this, whose type need be specified.

// C++11 and above.
using Member = int (Foo::*)(int, int);

// C++03 and below.
typedef int (Foo::*Member)(int, int);

does let you specify that the first element passed to the function will be a Foo* (and thus your method really takes 3 arguments, when you think of it, not just 2.

However there is another reason too, for forcing you to specify the type.

A function pointer might refer to a virtual function, in which case things can get quite complicated. Therefore, the very size of the in-memory representation changes depending on the type of function. Indeed, on Visual Studio, a function pointer's size might vary between 1 and 4 times the size of a regular pointer. This depends on whether the function is virtual, notably.

Therefore, the class the function refers to is part of the signature, and there is no work-around.

Sleeper answered 29/1, 2011 at 8:36 Comment(11)
@BЈовић: and it was such a good link too... oh well thankfully I knew a second article ;)Sleeper
New working link with downloads for The Function Pointers TutorialBettis
How would this look with C++11 "using" syntax?Misbehavior
@paulm: Good question, post edited. It's basically the same, except the name is extracted, and thus easier to find.Sleeper
@MatthieuM. Is this valid? Are you sure that the type of a non-static member function/data (not the type of a member pointer to that function/data) is Return(Class::*)(Args...) and not Return(Class::)(Args...)? If so, then is it true that this* is the reason for the * in the member's or member pointer's identical type (iow the fact that this* is a "pointer" that's always part of the signature)?Voltcoulomb
@Nikos: You can try the syntax yourself in a compiler: Return(Class::)(Args...) is not valid syntax. this (not this*) is always a pointer, despite never being null, and is passed as a pointer to methods. The invocation syntax is (this->*method)(args...) or (ref.*method)(args...), which is another layer of weird...Sleeper
Can you elaborate a bit more? In the next 3 examples A, B and C compile but not D. template <typename T, typename... A> using A = int (T::*)(A...); template <typename T, typename... A> using B = void (T::*)(A...); template <typename T> using C = int T::*; template <typename T> using D = void T::*; Weirdo
@PatrickFromberg: C and D are NOT function pointers (and thus outside this question), they are pointers to data-members. Different syntax, and a data-member cannot have for type void thus D is wrong.Sleeper
@MatthieuM. Yes, this is why I was surprised to see pointer to member used as parameter type for functions here: template< class M, class T> mem_fn(M T::* pm) noexcept. I was trying to figure out how std::invoke works exactly and it works for void returning functions. I should create a dedicated question actually.Weirdo
Late, I know, but actually I don't think this post answers the question; as far as I see, intention is not dropping the class name, but just dropping the asterisk to get the signature itself, not a pointer, see here. Answer: Impossible, not foreseen by the standard… (Though still nicely explained why the class specification cannot be dropped!)Evocative
@Aconcagua: Indeed, it looks I was slightly off.Sleeper
E
8

You can factor out the target class in modern C++ (post 11) by utilizing the 'typedefing' qualities of template aliases. What you need would look like like:

template<typename T>
using memberf_pointer = int (T::*)(int, int); 

Yet at the point of declaration, a pointer to member function utilizing this syntax would need to specify the target class:

// D is a member function taking (int, int) and returning int
memberf_pointer<foo> mp = &foo::D; 
Estreat answered 27/7, 2015 at 9:36 Comment(0)
L
1

The reason it doesn't work with your current syntax is that operator precedence dictates that you're referring to a function named foo::memberf_signature, not any sort of type.

I don't know for sure if you can do this or not, but I couldn't come up with any combination of parenthese that induced the code to compile with g++ 4.2.

Longfaced answered 28/1, 2011 at 19:57 Comment(1)
What about just ::memberf_signature maybe?Amontillado
C
1

It works for me:

#include <iostream>

class foo
  {
public:
  int g (int x, int y) { return x + y ; }
  } ;

typedef int (foo::*memberf_pointer)(int, int);

int main()
  {
  foo f ;
  memberf_pointer mp = &foo::g ;
  std::cout << (f.*mp) (5, 8) << std::endl ;
  }
Commute answered 28/1, 2011 at 21:24 Comment(2)
That's a typedef for a member function pointer. That work's for me too, but isn't what I'm after ;)Zwieback
OK, now I see what you want. But how do you plan to use this typedef, if you manage to create it?Commute
P
-3

Well basically it can't work (at least I know no way using g++); Using borland c++ compiler there would be the __closure keyword.

The reason why it does not compile is, that sizeof the functionpointer (on a x86 machine) occupies always <<32bits>>; but if you want to point to a class (interface) signature, the sizeof has to be 64bit: 32 bit for the this pointer (as the class interface is in the memory only once) and 32 bit for the actual function

But the __closure keyword is a bcb language 'hack' not standardized...

Priapism answered 28/1, 2011 at 20:8 Comment(3)
The fact that member function pointers and function pointers are of different sizes looks irrelevant to me. Typically, short and double are of different sizes, but you can typedef with both of them.Zeller
The this pointer is normally passed as a first, hidden parameter. I haven't heard of any compiler using a 64-bit pointer to munge the this pointer into the pointer to an object.Sarver
That's absolutely wrong. The this pointer is stored in the (e.g. MSC) ECX-Register on IA-32 target machine. Any pointer is always 32-bit on such a system.Zwieback

© 2022 - 2024 — McMap. All rights reserved.