17

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
  • 349
  • 1
  • 2
  • 11
  • 2
    Seems to work fine. You might want to include a full example including usage. Something that shows actual failure. – Captain Obvlious Aug 04 '13 at 04:40
  • Aside: you are assuming (on pain of undefined behaviour) that everything in your `std::function`s are function pointers, which isn't very sensible, because if they were, you could use the function pointer type instead, and not have all this trouble. – Caleth Sep 09 '20 at 16:55

2 Answers2

28

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;
}
Robert Mason
  • 3,949
  • 3
  • 31
  • 44
  • I'm currently using CLang 3.4 :D – Agustin Alvarez Aug 04 '13 at 04:53
  • 1
    In that case, if you change add `typedef fnType* fnTypeP;` and change your call to target to `target`, 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 :) – Robert Mason Aug 04 '13 at 04:58
  • Wait, can you simply convert a `std::function` to a function pointer? Does that work with stateful functions too? How does that work? – Mooing Duck Aug 04 '13 at 04:58
  • 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. – Agustin Alvarez Aug 04 '13 at 05:00
  • 8
    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. – Robert Mason Aug 04 '13 at 05:04
  • Yes i just tried that and returned null pointer :(. Is possible to capture lambda address, and &Class::function? – Agustin Alvarez Aug 04 '13 at 05:06
  • This is nice, but it only works if rtti is enabled – Treviño May 13 '21 at 18:59
9

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
Hugh Perkins
  • 7,975
  • 7
  • 63
  • 71
  • 1
    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 – XvXLuka222 Oct 15 '21 at 15:57