Function Pointer using "typedef" keyword with Function Address stored as Variable? (C++ 14, VS 2019)

---- Problem Summary ----

I'm attempting to create a callable function pointer inside of a function using templates for its return value and its arguments using C++ 14. I want the callable function pointer to return the templated type, and accept templated arguments. However, I also want the callable function pointer to point to an existing function at the address provided in an argument as an integer. This I am unsure of how to do. Below is an illustration of my current incomplete code:

1
2
3
4
5
6
7
template <class retTempl, class argTempl_1, class argTempl_2>
retTempl TEST_1(argTempl_1 value1, argTempl_2 value2, int addr) {

   typedef retTempl(*TEST_2)(argTempl_1, argTempl_2);
   return TEST_2(value1, value2);

}


In this case, "TEST_2" is the new function pointer that I want to call, and the "addr" argument is the address of an existing function that I want the "TEST_2" pointer to point to.

The current error that the VS 2019 compiler throws at me is the following:

 
Error   C2440   '<function-style-cast>': cannot convert from 'initializer list' to 'operationFunction'


I am unsure of exactly what it's trying to tell me, researching this error online leads to several possible causes, not all of which necessarily have to do with function pointers. However, this error may not even be relevant, as I have not been able to implement the function address yet. That is mostly what I want to focus on here.

---- My Attempts ----

I know that in order to make a function pointer created with the typedef keyword point to an actual function, you need to set the pointer variable equal to whichever function you want the pointer to point to. So for instance (example from https://www.section.io/engineering-education/function-pointers-in-c++/):

1
2
3
4
5
6
7
8
9
10
11
int add(int x ,int y)  
{  
   return x + y;  
}  
int main()  
{  
   int (*funcptr)(int, int);
   funcpointr = add;

   ...
}


However, here the function that I want the "TEST_2" pointer to point to is different every time that "TEST_1" is called, with a different return value and arguments. Therefore, I cannot simply set the "TEST_2" variable equal to the "addr" argument:

1
2
3
4
5
6
7
8
template <class retTempl, class argTempl_1, class argTempl_2>
retTempl TEST_1(argTempl_1 value1, argTempl_2 value2, int addr) {

   typedef retTempl(*TEST_2)(argTempl_1, argTempl_2);
   TEST_2 = addr;
   return TEST_2(value1, value2);

}


as this now supplies me with an additional VS error:

 
Error   C2513   'retTempl (__cdecl *)(argTempl_1,argTempl_2)': no variable declared before '='


I would assume that I would require another pointer variable here, perhaps from a list of function pointers that I want the "TEST_2" variable to point to, but at this point I would like to hear a second opinion before I resume.

Is what I'm trying to achieve here even possible to do with C++? Is there a different way that this should be done that I'm not aware of?

Thank you in advance for reading my post, any guidance is appreciated!
Last edited on
I apologize for the many edits of my post, I was learning how to properly write code snippets. It's my first time posting here.
This declaration
typedef int foo;
Declares a type name named foo, an abbreviation for int. Afterwards the declaration
foo x = 24;
declares a variable named x of type foo.

Similarly, this declaration
typedef retTempl(*TEST_2)(argTempl_1, argTempl_2);
Declares a type name named TEST_2, an abbreviation for the pointer-to-function type retTempl(*)(argTempl_1, argTempl_2).
Afterwards the declaration
TEST_2 pf;
Declares a variable named pf of type TEST_2.

(The immediate problem is that in the snippets above you never declared any variables.)

I also want the callable function pointer to point to an existing function at the address provided in an argument as an integer.

Now you should explain why you're asking for this: it's unusual and problematic.

Pointers to function cannot be reliably converted between integers or even object pointer types. To be strictly correct you have to go through any pointer-to-function type instead, here void(*)() is used.

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
#include <iostream>

double add(double x, double y) { return x + y; };
double sub(double x, double y) { return x + y; };

// Reinterpret pfn as pointer-to-function of type R(Args...) and call it with args... .
// The function type R(Args...) must exactly match the type of the pointed-to function.
template <typename R, typename... Args>
  R invoke_with_signature(void(*pfn)(), Args... args)
  { 
    return reinterpret_cast<R(*)(Args...)>(pfn)(args...); 
  } 
  
template <typename R, typename... Args>
  [[nodiscard]] void(*pfn_to_proc(R(*pf)(Args...)) noexcept)()
  { 
    return reinterpret_cast<void(*)()>(pf); 
  }
  
int main()
{
  void(*padd)() = pfn_to_proc(add);
  void(*psub)() = pfn_to_proc(sub);
  
  std::cout << invoke_with_signature<double, double, double>(padd, 2.0, 3.0) << '\n'; 
  std::cout << invoke_with_signature<double, double, double>(psub, 1.5, 0.2) << '\n';
}


FWIW this is not a normal thing to do.
If the addresses of your functions are really given to you as int and you have no choice, replace the reinterpret_cast with memcpy.

A complete solution would need four overloads of each function above to cover noexcept and C-style variadic functions.
Last edited on
@mbozzi Thank you very much for your prompt and detailed response;

Thank you very much for delineating what is wrong with my code here. Turns out the bigger problem was the fact that I was misinterpreting what the "typedef" keyword actually did, creating a type instead of a variable...

With your guidance here, I actually managed to get it to work, but in a far more simple way than I expected. I attempted using the "memcpy" command like you said, however I was experiencing some difficulties. However, when I tried the following:

1
2
3
4
5
6
7
8
template <class retTempl, class argTempl_1, class argTempl_2>
retTempl TEST_1(argTempl_1 value1, argTempl_2 value2, int addr) {

   typedef retTempl(*TEST_2)(argTempl_1, argTempl_2);
   TEST_2 test_function = (TEST_2)(addr);
   return test_function(value1, value2);

}


or:

1
2
3
4
5
6
7
8
template <class retTempl, class argTempl_1, class argTempl_2>
retTempl TEST_1(argTempl_1 value1, argTempl_2 value2, int addr) {

   typedef retTempl(*TEST_2)(argTempl_1, argTempl_2);
   TEST_2 test_function = reinterpret_cast<TEST_2>(addr);
   return test_function(value1, value2);

}


My program compiled & worked as intended, with all my functions being called as intended, however I'm sure that this is not the ideal way to do this. Therefore, I want to learn more about what your proper way would be.

Firstly, according to other people I asked, a variable of size "int" isn't actually large enough to fit memory addresses of functions on all types of systems. I heard some time ago that the type "uintptr_t" is larger and is made specifically for storing addresses. What are your reccomendations for storing memory addresses like this?

Secondly, the reason I'm even attempting something like this is because I'm attempting to write a scripting language interpreter. This specifically would be used to define operation functions for a variety of operators (such as "==" or "<" or ">"). That is why I wanted to be able to reference a variety of functions.

If there is a better way to do this that you think would better suit such a task, apart from what you've already sent, please let me know!

Also, purely out of curioustiy, what is the actual difference between casting the function pointer as:
 
TEST_2 test_function = (TEST_2)(addr);

as opposed to:
 
TEST_2 test_function = reinterpret_cast<TEST_2>(addr);

?

Working on projects like these in my free time has always been very fun for me, thank you for your guidance so far it has been very helpful!
Firstly, according to other people I asked, a variable of size "int" isn't actually large enough to fit memory addresses of functions on all types of systems. I heard some time ago that the type "uintptr_t" is larger and is made specifically for storing addresses.
std::uintptr_t is large enough to store object pointers. However function pointers are allowed (technically) to be larger than even std::uintptr_t, or impose stricter alignment requirements.

In the above I used void(*)() as a work-around. See line 17, which converts the type of any pointer to function pfn to void(*)(), which is always compatible.

Secondly, the reason I'm even attempting something like this is because I'm attempting to write a scripting language interpreter. This specifically would be used to define operation functions for a variety of operators (such as "==" or "<" or ">"). That is why I wanted to be able to reference a variety of functions.

Hopefully you can avoid treating pointers as integers at all, because that's fraught with error. Keep your pointers as pointers.

What is the actual difference

In this particular case, there is no difference (the behavior is the same).
Even so, prefer C++-style conversions because they are more likely to catch mistakes.
Last edited on
@mbozzi Thank you for your response;

Thank you so much for the detailed information you have provided me. I will go ahead and mark this forum entry as resolved now, since all of my original questions were addressed.

As a follow up, out of curiousity, apart from the fact that a value of "int" wouldn't be large enough to support all function address formats, what other errors could originate from this arrangment in relation to the address being stored in such a variable?

Thank you very much for all the information you have provided me during the course of this conversation, you have helped me out a lot!
On mainstream implementations,

(on any POSIX compatible system) a function pointer can be converted to void* or any other object pointer, or vice versa. If the implementation supports conversion in both directions, conversion to the original type yields the original value

https://en.cppreference.com/w/cpp/language/reinterpret_cast

and since an object pointer can be converted to any integral type large enough to hold all values of its type, using std::uintptr_t would work in practice.

For example:

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
#include <iostream>
#include <functional>
#include <cstdint>
#include <cstring>

template < typename FN > inline std::uintptr_t to_intptr( FN* pfn )
{ return reinterpret_cast<std::uintptr_t>( reinterpret_cast<const void*>(pfn) ) ; }

template < typename FN > inline FN* to_function_ptr( std::uintptr_t ptr_value )
{ return reinterpret_cast<FN*>( reinterpret_cast<void*>(ptr_value) ) ; }

template < typename RET, typename... ARGS >
RET invoke( std::uintptr_t fn_ptr_value, ARGS... args )
{ return to_function_ptr< RET(ARGS...) >(fn_ptr_value)( args... ) ; } // or: return std::invoke( to_function_ptr< RET(ARGS...) >(fn_ptr_value), args... ) ;

int main()
{
    const auto int_ptr = to_intptr(std::strlen) ;
    const auto strlen2 = to_function_ptr<decltype(std::strlen)>(int_ptr) ;
    std::cout << ( std::strlen == strlen2 ? "ok\n" : "error\n" ) ;

    const char cstr[] = "this is a null terminated byte string!" ;
    std::cout << cstr << '\n'
              << std::strlen(cstr) << " == " << invoke<std::size_t>( int_ptr, cstr ) << '\n' ;

    char cstr2[ sizeof(cstr) ] {} ;
    invoke< char* >( to_intptr(std::strcpy), cstr2, cstr ) ;
    std::cout << cstr2 << '\n'
              << ( invoke<int>( to_intptr(std::strcmp), cstr, cstr2 ) == 0 ? "ok\n" : "error\n" ) ;
}

https://coliru.stacked-crooked.com/a/32abf4177524bca3
Topic archived. No new replies allowed.