How to convert C++ member function pointer to C function pointer?

Pages: 12
#include <iostream>
using namespace std;

class Printer
{
public:
void println(const char* string_pointer) { cout << string_pointer << endl; }
};

int main(int argc, char** argv)
{
void(*println_ptr)(Printer*, const char*) = &Printer::println; //This line does not compile.
Printer printer;
println_ptr(&printer, "string");
}

Does exist a general function to convert C++ member function pointer to C function pointer?

Theoretically this should be possible.

I know that I can define and use C++ member function pointers, but C function pointers are more convenient, readable and maintainable.
Last edited on
I know that I can define and use C++ member function pointers, but C function pointers are more convenient, readable and maintainable.


Are they? The following looks an awful lot more readable and maintainable to me.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <functional>
using namespace std;

class Printer
{
public:
void println(const char* string_pointer) { cout << string_pointer << endl; }
};

int main(int argc, char** argv)
{
  auto println_function = &Printer::println;
  Printer printer;
  std::invoke(println_function, printer, "string");
}


If you must do it longhand:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <functional>
using namespace std;

class Printer
{
public:
void println(const char* string_pointer) { cout << string_pointer << endl; }
};

int main(int argc, char** argv)
{
  void(Printer::*println_ptr)(const char*) = &Printer::println;
  Printer printer;
  std::invoke(println_ptr, printer, "string");  

  // or alternatively called...
  (printer.*println_ptr)("string2");
}



If you mean that you want to take the member function, which is of type void (Printer::*)(const char*), but reach it through something of type void(Printer*)(Printer*, const char*) , I think you might be out of luck without doing something horrible.
Last edited on
Does exist a general function to convert C++ member function pointer to C function pointer?

No.

Because of issues with multiple and virtual inheritance, it is not possible to perform the conversion in general. In practice, pointer-to-members may differ in size depending on the class they're associated with.

Instead, erase the type of the callable object. The utility to do this is called std::function:
https://en.cppreference.com/w/cpp/utility/functional/function
Theoretically this should be possible.

No, it shouldn’t.

And it isn’t; a pointer to a stand-alone function and a pointer to a member function are not the same thing, neither in the language nor underneath in implementation.

In fact, the language specifically stipulates that pointers of different base types are not compatible. Since the PC and modern ARM processors handle addresses the same regardless, a lot of code does exist that type-puns different pointer types. (The Windows API is a big offender, actually — but it works because MSVC is designed to work on systems where it is known to work.)

Code that is meant to be truly cross-platform will not use pointer punning at all, because there do exist systems where it will fail!

When it comes to the underlying C++ object models, there aren’t many to choose from, but those that exist do so because they work well and are convenient. They do, however, require a bit more space to manage a pointer to member function, however, because the pointer isn’t just a pointer to executable space, but there is baggage to carry around: vtables and the like.

Hope this helps.
So tell me, how exactly my C++ code both compiles and translates to C code by the MSVC compiler (cl.exe) of Microsoft Visual Studio Enterprise 2019 then?

By the way that I just printed the sizeof a C++ member function pointer on a windows x64 build.

8 was printed on the cmd terminal window.

This is the sizeof of any C pointer in x64 build.

Seems to me that C++ member function pointer is just another C function pointer, so why the conversion is impossible?

Although when I printed the C++ member function pointer itself, 1 was printed on the cmd terminal window on x64 build.

That's strange. Can you explain this to me please?
Last edited on
So tell me, how exactly my C++ code both compiles and translates to C code by the MSVC compiler (cl.exe) of Microsoft Visual Studio Enterprise 2019 then?


Does it? The last time I knew of that being the case was back in the days of CFront.

Is that some special option you found? To turn C++ code into C code?

The MSVC compiler typically turns C++ into assembly; how did you get it to translate C++ into C?


Seems to me that C++ member function pointer is just another C function pointer, so why the conversion is impossible?

It's not impossible. Just a really bad idea. You can point at any piece of memory and INSIST to the compiler that it's NOT what the compiler thinks it is. You can INSIST that it's something else. In doing so, you throw away all the protections and checks of the compiler. Here you go:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;

class Printer
{
public:
void println(const char* string_pointer) { cout << string_pointer << endl; }
};

int main(int argc, char** argv)
{
  void(*println_ptr)(Printer*, const char*) = reinterpret_cast<void (*)(Printer*, const char*)>(&Printer::println);
  Printer printer;
  println_ptr(&printer, "string");
}

Good luck. Generally, when people see <reinterpet_cast> in code, it's a sign that something has gone very very wrong in the design phase.

To me, this looks a lot less readable and maintainable (and also much, much more dangerous) than auto println_ptr = &Printer::println; In C++, writing function pointers at all is generally a bad sign. C++ has easier, safer facilities in the language.

The whole thing feels like you're thinking in C, and you're trying to write C code, but you're doing it with C++. This is something I see a lot when people move languages; they keep thinking in their old language, design code that suits their old language, and then create something monstrous.

You're definitely not thinking in C++; I can tell this because you're trying to use implementation details like the size of a variable to deduce what the language should allow you to do.
Last edited on
I tried reinterpret_cast.

The code still does not compile.

Also if MSVC compiler cannot both compile and translate my C++ code to C code then how does MSVC compiler compile and translate my C++ code to assembly code then?
The above code compiles and runs. try it: http://cpp.sh/9jbi5
Don't trust it, though. It's monstrous. If it works, it's by luck and chance.

You did fix this mistake, right?
void(*println_ptr)(Printer*, const char*) = &Printer::print; //This line does not compile.
Where you tried to get the address of the function named Printer::print ? Because the funciton is actually named println


Also if MSVC compiler cannot both compile and translate my C++ code to C code then how does MSVC compiler compile and translate my C++ code to assembly code then?


CFront worked like that, turning very early C++ code into C code first, back in 1983.

This is 2019. Compilers can turn C++ code directly into assembly. Some compilers do have an intermediate language when turned C++ into assembly; that intermediate language is not C.

https://en.wikipedia.org/wiki/Intermediate_representation - you'll read here that for many languages that are compiled, C is a popular intermediate representation - that's not the case for C++

To put it another way; assembly doesn't have to be made out of C code.

Also if MSVC compiler cannot both compile and translate my C++ code to C code then how does MSVC compiler compile and translate my C++ code to assembly code then?

Genuinely a context switch for me. It appears that until this moment, you believed that to get assembly code you HAD to start with C code? Every day's a school day; we all carry misconceptions we're not even aware of until they run into something.
Last edited on
By the way that I just printed the sizeof a C++ member function pointer on a windows x64 build.

8 was printed on the cmd terminal window.

This is the sizeof of any C pointer in x64 build.

Seems to me that C++ member function pointer is just another C function pointer, so why the conversion is impossible?


This is a failure of reasoning in action.

You have not researched what @Duthomhas has written above.

"Seems to me..." is the opening of abandonment of logic in this case. The documentation on the C++ language makes it clear that member function pointers are not just another C function pointer.

The size you happen to get from one compiler is not representative.

I've seen sizes vary greatly for pointer to member functions, as much as triple the size of the platform's atomic pointer. The reason is that the format for this pointer is not specified in the language design, it is left as an implementation detail. This is similar to the format of a floating point number, which is also left to the implementation, and is not the same from one compiler to the next on various platforms, even though if you write on modern Windows machines you'll almost always see the same format.

If C were to make a call into a C++ member function, it would be by fiat (which just happens to be the way C does things sometimes). If a C application were designed to somewhat emulate what is happening, there would be a required structure of data as the first parameter to that function (which C++ treats as an automatic, assumed parameter to the function call, where C does not).

This means, at the root of the problem, is that the analogous signature of a comparable C function to a C++ function differs, and it does so with a type (and data layout) that C is not equipped to understand.

That said, there are machinations one can go through to perform such calls, but it is not a practical act. It may be a curious study, but one that will eventually exhaust all associated time.

When working in various languages to bridge to C++ code, there is a common tactic used. This is typical of Swift to C++ (on Apple). Some "void pointer" is used to represent the instance to a C++ object. These bits mean nothing to the calling language, but this pointer is passed untouched to a C function.

This C function uses the void pointer to cast into a C++ instance pointer, from which a member function call can be made. This is possible because C++ compilers can declare C functions, but still compile with full C++ "awareness". This is a simple but effective model for calling a C++ member function on an instance from a language which has no such ability, using a C function for each member function to be called.

The concept can be generically wrapped into some mechanism for more generalized translation.


Also if MSVC compiler cannot both compile and translate my C++ code to C code then how does MSVC compiler compile and translate my C++ code to assembly code then?


First, C was originally purpose built to be an assembler for writing the UNIX operating system. Translating to low level or elemental C is nearly identical in concept to translating into assembler. The low level of C is a kind of assembler, independent of the CPU. That is it's historical origin.

Which is to say, relative to your question, there really isn't a difference between translation from C++ into either target. However, there is no mystery as to why C isn't an option on the MSVC compiler for output - as there isn't an option for the Niagra processor, or the Cray-2 CPU. There simply hasn't been a reason to provide that option. Even if it did, the output to C would not look as you expect. It would be very low level C, and as such, look more like assembler output than C code written by humans.

That said, recall my point that a member function call is merely a call to a function with an assumed parameter of an instance. That isn't a complete definition (I'm not a language lawyer writing the rules), but it is informative as a start. It does leave the impression that one could simply fashion a call (an assembler level call to a function), and pass, at least, that first required pointer to an instance of a C++ object.

As long as the rest of the function itself were then able to continue behaving according to the rules of that object, perhaps that could work, but there are many catches which @Duthomhas eluded, and you should research.

For example, if, in that function, a call to a member function is made that happens to be a virtual function. What is done then? How would C "know" what to do?

In the case of a C++ compiler generating assembler (or C output), it is the C++ compiler that would "know" what to do and fashion the appropriate code to follow the protocols.


Although when I printed the C++ member function pointer itself, 1 was printed on the cmd terminal window on x64 build.

That's strange. Can you explain this to me please?




At this point it seems to me you need to develop a new skill not yet demonstrated in your questions.

Research.

The documentation of the C++ language makes it clear that certain details are left to the implementation, and are not standardized. The size of a member function pointer varies from compiler to compiler, even on the same platform.

This is no different that the fact that C++ and C make no specific guarantee of the floating point format, even though on most any compiler in Windows you'll find that is IEEE's 754 format, and I don't recall ever seeing anything else there.

The fact is, it could be any format for floating point representation - which is to say the bits found inside a floating point value may not be what you expect from one platform to another.

This is the same for pointers to members - they are not the same from one compiler to another even on the same platform (though some compilers mirror each other in this aspect).

What you find by a single experiment proves nothing. There are tables available on the internet where that comparison has been done on various compilers to compare the size of the pointer to member function, illustrating not only the variance from one compiler to the next, but in some cases how that size changed in various versions of a particular compiler.

That is the kind of research I suggest you learn to implement, because you're going to be asking questions like these, with repeated versions of "...but I see X, so prove why what I said isn't true..." - without realizing that knowledge is already part of the research, and well known.

We do love to explain things around here, don't get me wrong.

I sense you're at a point of study where you're about to transition upward to a new level, moving fast and thinking hard. This is great.

However, you also demonstrate recognizable habits of thought from that lower level you need to shed in order to soar above that level. Many of us here have been doing this for decades and know the material well. Some responded in this thread. They know exactly what they're talking about. It can be hard to tell the difference between them and others with a few more years than you, but not yet decades of experience and knowledge.

For this, in the anonymous cyberspace, we must re-verify as we study.
I already told you that the some implementations might make the size of a pointer-to-member dependent on the class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#define STRINGIFY0(x) #x
#define STRINGIFY(x)  STRINGIFY0(x)
#define PRINT_SIZE(x) \
  std::printf("sizeof (%s): %zu\n", STRINGIFY(x), sizeof(x))

#include <cstdio>

int main() 
{
  {
    struct A {};
    std::puts("no inheritance");
    PRINT_SIZE(void(A::*)());
  }
  
  {
    struct A     {}; 
    struct B : A {};
    std::puts("single inheritance");
    PRINT_SIZE(void(B::*)());
  }
  
  {
    struct A {};
    struct B {};
    struct C : A, B {};
    std::puts("multiple inheritance");
    PRINT_SIZE(void(C::*)());
  }
   
  {
    struct A {};
    struct B : virtual A {};
    struct C : virtual A {};
    struct D : B, C {};
    
    std::puts("virtual inheritance");
    PRINT_SIZE(void (D::*)());
  }
  
  { 
    struct A;
    puts("forward-declared class");
    PRINT_SIZE(void(A::*)());
    std::puts("");
  }
}
Possible output:
no inheritance
sizeof (void(A::*)()): 8
single inheritance
sizeof (void(B::*)()): 8
multiple inheritance
sizeof (void(C::*)()): 16
virtual inheritance
sizeof (void (D::*)()): 16
forward-declared class
sizeof (void(A::*)()): 24


Live demo: https://rextester.com/LRKH3271
Last edited on
Okay, I corrected to &Printer::println, but reinterpret_cast still doesn't work.

And I do understand that the C++ language has many different compilers to many different platforms, and all the compilers implement member function pointers in different ways, data structures and algorithms, but this doesn't mean that the conversion from C++ member function pointer to C function pointer isn't possible.

I say that every C++ compiler can convert C++ member function pointer to C function pointer, no matter how the C++ compiler does implement C++ member function pointer and C function pointer.

Every C++ compiler will do the conversion in different way though due to the varying implementations of C++ member function pointer and C function pointer in every compiler to every platform.
Last edited on
Okay, I corrected to &Printer::println, but reinterpret_cast still doesn't work.


In what way does it "not work"? Does it compile? As you can see, it works in the link I provided (although it works by chance - not by design). Here it is again, working, in a different place - https://ideone.com/5Wbsgc


I say that every C++ compiler can convert C++ member function pointer to C function pointer, no matter how the C++ compiler does implement C++ member function pointer and C function pointer.

What is the difference between a C++ member function pointer and a C function pointer? Ultimately, they're both just numbers. A memory address. That's what EVERY pointer is, ultimately. A way of indicating a particluar place in memory. A number. Just a single number.

So when you say
every C++ compiler can convert C++ member function pointer to C function pointer
, what does that even mean?
Last edited on
I have answered my question!

This topic can be archived now.

I am using Microsoft Visual Studio Enterprise Edition 2019.

All the code I write is passed to the MSVC compiler (cl.exe) as input text files.

Visual C++ member functions are exactly C functions.

When the MSVC compiler encounters a definition of a C++ member function, the MSVC compiler simply translates it to C function.

Weird that in Visual C++, member function pointers are C function pointers, but they define only one conversion/cast operator to bool type and this operator is implicit and that's it.

Member function pointers in Visual C++ do not define conversion/cast operators to any other pointer type neither implicit nor explicit.

I can't even use reinterpret_cast to convert C++ member function pointer to C function pointer, but I have found a work around.

This is weird, because any other pointer of type X (X*) is implicitly converted and casted to void* and explicitly to Y*.

The work around is to get the address of the member function pointer and get a pointer to pointer to member function, this pointer can be reinterpret casted to pointer to pointer to C function and then simply dereference the pointer to get the pointer to the C function.

My above C++ code compiled and gave the desired output when the first line of function main changed from:

void(*println_ptr)(Printer*,const char*) = &Printer::println;

to

void(Printer::*member_println_ptr)(const char*) = &Printer::println;
void(*println_ptr)(Printer*const,const char*) = *reinterpret_cast<void(**)(Printer*const,const char*)>(&member_println_ptr);

But this line doesn't compile:

void(*println_ptr)(Printer*const, const char*) = reinterpret_cast<void(*)(Printer*const, const char*)>(&Printer::println);
Last edited on
Your code is not guaranteed to work on any compiler, ever.

I do hope this was all just an academic exercise; if code like this came up in a code review, I'd have serious misgivings about that software engineer's continued employment...

The work around is to get the address...

The work around is to just write clear, correct C++ code.


I know that I can define and use C++ member function pointers, but C function pointers are more convenient, readable and maintainable.

Objectively not true, from the above discussions. Seriously, just look at this mess:
1
2
void(Printer::*member_println_ptr)(const char*) = &Printer::println;
void(*println_ptr)(Printer*const,const char*) = *reinterpret_cast<void(**)(Printer*const,const char*)>(&member_println_ptr);
Last edited on
SPP wrote:
Visual C++ member functions are exactly C functions.

When the MSVC compiler encounters a definition of a C++ member function, the MSVC compiler simply translates it to C function.

Look, you can believe and do what you want.

But don’t come here spouting nonsense. Because that is objectively not true.

SPP wrote:
I know that I can define and use C++ member function pointers, but C function pointers are more convenient, readable and maintainable.

They are none of those things with respect to C++ member function pointers.
  • The only increased “convenience” is a perceived one, due to throwing away state.
  • They are no more or less readable than any other function pointer to anyone who knows their salt.
  • Language features/miswarts/whatever has absolutely nothing to do with maintainability.
    And frankly, the suggestion that it is shows very poorly for your understanding of the subject.

If all you want is a C-style interface to expose in a DLL or something, then write a function to do it using the C-style methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// cprinter.h
#ifndef CPRINTER_H
#define CPRINTER_H

#ifdef __cplusplus
extern "C" {
#endif

typedef void* Printer;
printer create_printer();
void destroy_printer( Printer );
void printer_println( Printer, const char* );

#ifdef __cplusplus
}
#endif

#endif 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// cprinter.cpp

class Printer
{
  ...
};

extern "C"
{

  void printer_println( Printer* printer, const char* s )
  {
    return printer->println( s );
  }

}

Good luck now.
@SPP, you just dove into the rabbit hole and I'm afraid no one is going to be able to get you out.

I can state with absolute certainty that you don't actually know what you're doing, as @Repeater has said, and that your conclusions aren't correct.

What you've got is a hack that works in a specific circumstance. You're going to realize it doesn't work in others once you begin to rely upon it.

Like @Repeater, I'd have serious trouble employing someone who works and thinks this way. You're in for quite a long, winding road back to reality.

You've completely ignored @mbozzi's list of findings.

You've not attempted this on virtual functions, abstract base classes, or other cases.

Ultimately, modern C++ discourages the use of such pointers anyway, and they're actually not all that necessary. Your time would be better spent learning what that means.
What is the difference between a C++ member function pointer and a C function pointer? Ultimately, they're both just numbers. A memory address. That's what EVERY pointer is, ultimately. A way of indicating a particluar place in memory. A number. Just a single number.
A member function pointer, in general, is a data structure more complicated than a mere pointer.

A member function pointer can only be converted to an actual procedure address if devirtualization is possible. This is because the address of the procedure and the address of this depends on the dynamic type of the implicit object argument, which is not available in general until the point of the call.

In any event, OP does not seem to be concerned about the quality of his solution, and will be satisfied with whatever appears to work right now.

OP, here's what you asked for. Go have fun with some fundamentally broken code:
http://coliru.stacked-crooked.com/a/70c848b5176b9d2f
Last edited on
I must say that while getting into the intricacies of function pointers is on my "to learn" list, it isn't anywhere near the top.

The explanations here certainly by the Terrific Trio sure were helpful (for me) to read, the OP missed out on a bounty of good information to meditate on.
And what if I tell you that theoretically the C++ compiler can generate a C function that invokes the C++ member function and let the expression to evaluate to pointer to the compiler generated C function instead of C++ member function pointer.

For my C++ code, I would expect that the C++ compiler is smart enough to generate:

void println(Printer*const Printer_pointer, const char* string_pointer)
{
Printer_pointer->println(string_pointer);
}

And the &Printer::println expression simply becomes &println.

This solution will always work, no matter how member function pointer and member function is implemented by the C++ compiler.

This will work also with virtual functions and vtables.

Also I was curious about how the MSVC compiler implements non virtual member functions and member function pointers.

Also I can write a macro or template function that shortens the code that converts C++ member function pointer to C function pointer and also makes the code more readable.

I do know what to do in practice and what not to do in practice.

Thank you everyone for helping me.
Last edited on
Pages: 12