I have a C++ function that takes a std::function
as an input argument.
Specifically, a std::function<void (const Message&, Error)>
.
In my use-case, the caller may bind the std::function
to either a free function or a member function.
(I'm not experienced with std::bind
or std::function
, so I found it noteworthy that the same object type, std::function<void (const Message&, Error)>
, can be bound to a free function as well as a member function -- the latter by using std::bind
. I found it interesting because it seemed to abstract away the difference between a function pointer and a member function pointer (at least it gave me that impression))
For my debugging need, it would be useful to log a hash, something unique, associated with the std::function
input argument.
Here's where I quickly realized I can't escape that fundamental difference between free function pointers and member function pointers.
I can get the underlying void (*)(const Message&, Error)
free function pointer using std::function::target<void (*)(const Message&, Error)>()
, which serves my needs as a unique hash.
But that doesn't work if the std::function<void (const Message&, Error)>
is bound to a member function.
In my head, I reasoned that if the std::function<void (const Message&, Error)>
was bound to a class Foo
member function, then std::function::target<void (Foo::*)(const Message&, Error)>()
would return the pointer to a member function pointer -- but that didn't seem to be the case.
Which leads to my question: is there any way to generically get a unique hash from a std::function
instance regardless whether it's bound to a free function or a member function?
#include <functional>
#include <iostream>
using namespace std;
struct Message {
int i_;
};
struct Error {
char c_;
};
class Foo {
public:
void print(const Message& m, Error e) {
cout << "member func: " << m.i_ << " " << e.c_ << endl;
}
};
void print(const Message& m, Error e) {
cout << "free func: " << m.i_ << " " << e.c_ << endl;
};
void doWork(function<void (const Message&, Error)> f) {
// I can invoke f regardless of whether it's been bound to a free function or
// a member function...
{
Message m{42};
Error e{'x'};
f(m, e);
}
// ...but since I don't know whether f is bound to a free function or a member
// function, I can't use std::function::target<>() to generically get a
// function pointer, whose (void*) value would have served my need for a
// hash...
{
typedef void (*Fptr)(const Message&, Error);
typedef void (Foo::*Mfptr)(const Message&, Error);
Fptr* fptr = f.target<Fptr>();
Mfptr* mfptr = nullptr;
cout << "free func target: " << (void*)fptr << endl;
if (fptr) {
cout << "free func hash: " << (void*)*fptr << endl;
}
else {
// ...moreover, when f is bound to a Foo member function (using
// std::bind), std::function::target<>() doesn't return a Foo member
// function pointer either...I can't reason why not.
// (this also isn't scalable because in future, f may be bound to a
// class Bar or class Baz member function)
mfptr = f.target<Mfptr>();
cout << "not a free function; checking for a Foo member function" << endl;
cout << "member func target: " << (void*)mfptr << endl;
if (mfptr) {
cout << "member func hash: " << (void*)*mfptr << endl;
}
}
}
}
int main()
{
{
function<void (const Message&, Error)> f = print;
doWork(f);
}
cout << "---" << endl;
{
Foo foo;
function<void (const Message&, Error)> f = bind(&Foo::print,
&foo,
placeholders::_1,
placeholders::_2);
doWork(f);
}
return 0;
}
Compilation and output:
$ g++ --version && g++ -g ./main.cpp && ./a.out
g++ (Debian 8.3.0-6) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
free func: 42 x
free func target: 0x7ffda4547bf0
free func hash: 0x55db499c51e5
---
member func: 42 x
free func target: 0
not a free function; checking for a Foo member function
member func target: 0
std::function::target_type().name()
? – Sotossize_t addr = (size_t)my_func;
. If two functions are equal, they have the same address. – DweltdoWork()
, so I can only work with whateverstd::function
provides. – Gobblebind
was created, you may be able to obtain its type viadecltype(std::bind(...))
. And thentarget<...>
will point to the instance. The problem is, it is likely allocated locally inside thestd::function
, meaning the address of target will change for each instance ofstd::function
and there isn't any suitable interface for an outputbind
to tell anything about its intrinsics. – Penton