Cast function pointer to void (*) (), then redraw to the original type

This question is for testing purposes, nothing more.

I'm currently trying to store function pointers with a different number of parameters (and these parameters can have different types).

Basically, I encoded the following code snippet in C ++ 11:

#include <functional> #include <iostream> void fct(int nb, char c, int nb2, int nb3) { std::cout << nb << c << nb2 << nb3 << std::endl; } template <typename... Args> void call(void (*f)(), Args... args) { (reinterpret_cast<void(*)(Args...)>(f))(args...); } int main(void) { call(reinterpret_cast<void(*)()>(&fct), 42, 'c', 19, 94); } 

I will convert the function pointer void(*)(int, char, int, int) to a generic function pointer void(*)() . Then, using the template's variable parameters, I simply rewrite the pointer to its original type and call the function with some parameters.

This code compiles and runs. In most cases, it displays good values. However, this code gives me some Valgrind errors on Mac OS (regarding uninitialized values), and sometimes it displays some unexpected garbage.

 ==52187== Conditional jump or move depends on uninitialised value(s) ==52187== at 0x1004E4C3F: _platform_memchr$VARIANT$Haswell (in /usr/lib/system/libsystem_platform.dylib) ==52187== by 0x1002D8B96: __sfvwrite (in /usr/lib/system/libsystem_c.dylib) ==52187== by 0x1002D90AA: fwrite (in /usr/lib/system/libsystem_c.dylib) ==52187== by 0x100025D29: std::__1::__stdoutbuf<char>::overflow(int) (in /usr/lib/libc++.1.dylib) ==52187== by 0x10001B91C: std::__1::basic_streambuf<char, std::__1::char_traits<char> >::xsputn(char const*, long) (in /usr/lib/libc++.1.dylib) ==52187== by 0x10003BDB0: std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> > std::__1::__pad_and_output<char, std::__1::char_traits<char> >(std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> >, char const*, char const*, char const*, std::__1::ios_base&, char) (in /usr/lib/libc++.1.dylib) ==52187== by 0x10003B9A7: std::__1::num_put<char, std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> > >::do_put(std::__1::ostreambuf_iterator<char, std::__1::char_traits<char> >, std::__1::ios_base&, char, long) const (in /usr/lib/libc++.1.dylib) ==52187== by 0x1000217A4: std::__1::basic_ostream<char, std::__1::char_traits<char> >::operator<<(int) (in /usr/lib/libc++.1.dylib) ==52187== by 0x1000011E8: fct(int, char, int, int) (in ./a.out) ==52187== by 0x1000013C2: void call<int, char, int, int>(void (*)(), int, char, int, int) (in ./a.out) ==52187== by 0x100001257: main (in ./a.out) 

I find this quite interesting because when I call the function, I reworked a pointer to its original type. I thought it looked like the lithium type was void* and then converted it to the original data type.

What is wrong with my code? Can we point to pointers to a pointer to void(*)() , and then rewrite this pointer to the original signature of the function pointer?

If not, are there other ways to achieve this? I am not interested in std::bind , which is not what I want.

+5
source share
2 answers

Stepping on a limb and guess what you did to make him fail ...

 #include <functional> #include <iostream> void fct(int nb, char c, int nb2, std::string nb3) { std::cout << nb << c << nb2 << nb3 << std::endl; } template <typename... Args> void call(void (*f)(), Args... args) { (reinterpret_cast<void(*)(Args...)>(f))(args...); } int main(void) { call(reinterpret_cast<void(*)()>(&fct), 42, 'c', 19, "foobar"); } 

This will not work because "foobar" never converts to std::string ... how can the compiler know if it passes through Args... ?

I'm not sure exactly how std::string gets hit on the call stack by the caller (the link to the string will be indicated as a pointer), but I suspect that this is more than just a pointer to char* , When the caller pops that pointer to char* , waiting for the whole string member, it fades away.

I think if you switch to

 void fct(int nb, char c, int nb2, char* nb3) 

or

 call(reinterpret_cast<void(*)()>(&fct), 42, 'c', 19, std::string("foobar")); 

then it might work.

+1
source

You said that you are also interested in alternatives. Personally, I would not have implemented such things, even if it worked perfectly, and function pointers and reinterpret_casts are things that I try to avoid. I have not tested this code, but I thought:

 #include <functional> #include <iostream> #include <boost/any.hpp> template <typename... Args> void call(boost::any clbl, Args... args) { auto f = boost::any_cast<std::function<void(Args...)>>(clbl); f(args...); } int main(void) { std::function<void(int, char, int, int)> func = fct; call(boost::any(func), 42, 'c', 19, 94); } 

Edit: this code, combined with your fct definition, works correctly and runs valgrind on Fedora, compiled with clang35.

+1
source

All Articles