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
The following code:
#include <functional>
#include <iostream>
#include <vector>
#include <string>
#include <cstdint>
int f(int a) { return -a; }
int f2(int a) { return a; }
int main() {
std::vector<std::function<int(int)>> fn{
f,
f,
f2,
f2,
[](int a) {return -a;},
[](int a) {return -a;},
[](int a) {return -a;},
};
for (auto&& a : fn) {
const auto t = a.target<int(*)(int)>();
const auto hash = t ?
(size_t)(uintptr_t)(void*)*t :
a.target_type().hash_code();
std::cout << hash << '\n';
}
}
Initialized vector of two f functions, two f2 functions, and 3 lambda functions. Thus we are expecting two same hashes, two same hashes, and each lambda is a new type - 3 different hashes. The code outputs:
4198918
4198918
4198932
4198932
11513669940284151167
7180698749978361212
13008242069459866308