C++ trying to get function address from a std::function
Solution 1
You need to use the template
keyword when you call target:
#include <functional>
#include <iostream>
template<typename T>
size_t getAddress(std::function<void (T &)> f) {
typedef void (fnType)(T &);
fnType ** fnPointer = f.template target<fnType*>();
return (size_t) *fnPointer;
}
void foo(int& a) {
a = 0;
}
int main() {
std::function<void(int&)> f = &foo;
std::cout << (size_t)&foo << std::endl << getAddress(f) << std::endl;
return 0;
}
Hint: When you have problems with C++ syntax, I suggest you use clang++
to compile your code. If you play around with how your write the code it will usually point you in the write direction to fix the error (when it can figure out what you are doing).
I also suggest that you use variadic templates to make your function a bit more general:
template<typename T, typename... U>
size_t getAddress(std::function<T(U...)> f) {
typedef T(fnType)(U...);
fnType ** fnPointer = f.template target<fnType*>();
return (size_t) *fnPointer;
}
Solution 2
A std::function
is just an object, albeit disguised as not-an-object. So, we can take the address of this object, and this is invariant across copies and such.
To get the pointer, we need a bit of casting. Eg given a function f1
, we can print the implicit pointer by doing:
std::cout << "f1: " << *(long *)(char *)&f1 << std::endl;
What this does is:
- takes the address of f1. f1 is itself a pointer to the function object, albeit disguised as a primitive
- so this address is a pointer to the pointer to the function
- what we want is the underlying pointer to the function
- cast the pointer to pointer into a pointer to char
- thence to a pointer to long
- now, assuming that pointer sizes are
long
s, we can derefernce this pointer to long, and get the underlying address associated with the function object.
Given two std::function<void()>
s f1
and f2
, we can do things like:
std::cout << "f1: " << *(long *)(char *)&f1 << std::endl;
std::cout << "f2: " << *(long *)(char *)&f2 << std::endl;
std::function<void()> f1copy = f1;
std::cout << "\nafter copy f1 into f1copy:" << std::endl;
std::cout << "addresses of f1 and f1copy differ: " << &f1 << " " << &f1copy << std::endl;
std::cout << "but underlying pointers identical: " <<
*(long *)(char *)&f1 << " " << *(long *)(char *)(&f1copy) << std::endl;
std::cout << "\n after assign f2 to f1copy" << std::endl;
f1copy = f2;
std::cout << "now the underlying pointer of f1copy matches f2's:" << std::endl;
std::cout <<
*(long *)(char *)&f2 << " " << *(long *)(char *)&f1copy << std::endl;
Example output:
f1: 4439003784
f2: 4439003912
after copy f1 into f1copy:
addresses of f1 and f1copy differ: 0x7fff572ab970 0x7fff572ab910
but underlying pointers identical: 4439003784 4439003784
after assign f2 to f1copy
now the underlying pointer of f1copy matches f2's:
4439003912 4439003912
Agustin Alvarez
Updated on June 04, 2022Comments
-
Agustin Alvarez almost 2 years
i'm trying to find the address of a function from a std::function.
The first solution was:
size_t getAddress(std::function<void (void)> function) { typedef void (fnType)(void); fnType ** fnPointer = function.target<fnType *>(); return (size_t) *fnPointer; }
But that only works for function with (void ()) signature, since i need for function that signature are (void (Type &)), i tried to do
template<typename T> size_t getAddress(std::function<void (T &)> function) { typedef void (fnType)(T &); fnType ** fnPointer = function.target<fnType *>(); return (size_t) *fnPointer; }
And i get "Error - expected '(' for function-style cast or type construction"
Update: Is any way to capture member class address? for class members i'm using:
template<typename Clazz, typename Return, typename ...Arguments> size_t getMemberAddress(std::function<Return (Clazz::*)(Arguments...)> & executor) { typedef Return (Clazz::*fnType)(Arguments...); fnType ** fnPointer = executor.template target<fnType *>(); if (fnPointer != nullptr) { return (size_t) * fnPointer; } return 0; }
Update: To capture lambda i'm using
template <typename Function> struct function_traits : public function_traits<decltype(&Function::operator())> { }; template <typename ClassType, typename ReturnType, typename... Args> struct function_traits<ReturnType(ClassType::*)(Args...) const> { typedef ReturnType (*pointer)(Args...); typedef std::function<ReturnType(Args...)> function; }; template <typename Function> typename function_traits<Function>::function to_function (Function & lambda) { return static_cast<typename function_traits<Function>::function>(lambda); } template <typename Lambda> size_t getAddress(Lambda lambda) { auto function = new decltype(to_function(lambda))(to_function(lambda)); void * func = static_cast<void *>(function); return (size_t)func; } std::cout << getAddress([] { std::cout << "Hello" << std::endl;}) << std::endl;
-
Agustin Alvarez almost 11 yearsI'm currently using CLang 3.4 :D
-
Robert Mason almost 11 yearsIn that case, if you change add
typedef fnType* fnTypeP;
and change your call to target totarget<fnTypeP>
, clang will tell you to add intemplate
and where. If you make the parser's job easy enough it can figure out what you're trying to do :) -
Mooing Duck almost 11 yearsWait, can you simply convert a
std::function
to a function pointer? Does that work with stateful functions too? How does that work? -
Agustin Alvarez almost 11 yearsI'm using function address to store them into a map and able to remove it. You can convert a std::function into a C function pointer.
-
Robert Mason almost 11 yearsNo, you can't in all cases. For example, if I bind the function into a lambda with capture,
target()
will return a null pointer, and the function will segfault. Use with caution. -
Agustin Alvarez almost 11 yearsYes i just tried that and returned null pointer :(. Is possible to capture lambda address, and &Class::function?
-
Treviño about 3 yearsThis is nice, but it only works if rtti is enabled
-
XvXLuka222 over 2 yearsit works, thank you! but I must admit that this can be unsafe though, for example if the compiler uses a different implementation, then there might be a chance that the first member is not a pointer but instead just padding or something like that (even though I don't think that this may happen). But still good answer 👍