C++ trying to get function address from a std::function

15,240

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 longs, 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
Share:
15,240
Agustin Alvarez
Author by

Agustin Alvarez

Updated on June 04, 2022

Comments

  • Agustin Alvarez
    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
    Agustin Alvarez almost 11 years
    I'm currently using CLang 3.4 :D
  • Robert Mason
    Robert Mason almost 11 years
    In that case, if you change add typedef fnType* fnTypeP; and change your call to target to target<fnTypeP>, clang will tell you to add in template and where. If you make the parser's job easy enough it can figure out what you're trying to do :)
  • Mooing Duck
    Mooing Duck almost 11 years
    Wait, can you simply convert a std::function to a function pointer? Does that work with stateful functions too? How does that work?
  • Agustin Alvarez
    Agustin Alvarez almost 11 years
    I'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
    Robert Mason almost 11 years
    No, 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
    Agustin Alvarez almost 11 years
    Yes i just tried that and returned null pointer :(. Is possible to capture lambda address, and &Class::function?
  • Treviño
    Treviño about 3 years
    This is nice, but it only works if rtti is enabled
  • XvXLuka222
    XvXLuka222 over 2 years
    it 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 👍