Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Is there a way to create a hash from a std::function?

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))

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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

>Solution :

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 :
            std::hash<std::string_view>{}(a.target_type().name());
        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
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading