4

I need to get a unique ID which I could use as a template argument (which means it must be statically determined at compile-time), for each member function of my class. My first idea was to use the index of the function within the class, but as far as I can tell, there's no way to obtain it. So, I'm trying to use the offset of the member function within the class, but I cannot figure out how to obtain it:

#include <iostream>
#include <typeinfo>

class MyClass {
    template <typename F>
    size_t getUniqueId(F f) {
        return ...something unique, maybe based on f's offset?...
    }

public:
    void method1(bool) {
        std::cout << "My ID is " << getUniqueId(&MyClass::method1) << '\n';
    }

    void method2(int) {
        std::cout << "My ID is " << getUniqueId(&MyClass::method2) << '\n';
    }

    void method3(bool) {
        std::cout << "My ID is " << getUniqueId(&MyClass::method3) << '\n';
    }
};
int main()
{
    MyClass a;
    a.method1(true);
    a.method2(1);
    a.method3(true);
}

I tried implementing getUniqueId() like this:

    template <typename F>
    size_t getUniqueId(F f) {
        return typeid(f).hash_code();
    }

but (unsurprisinlgy) this doesn't work, because it returns the same value for method1 and method3, since their signature is the same.

I think I will have to resort to using preprocessor macros (I could just use the __LINE__ one to get a unique ID from within the method), but a clean C++ solution would be preferable.

5
  • 6
    Can you explain more about the actual problem you are trying to solve ? This could be an XY problem. Commented Nov 23 at 9:12
  • 1
    ¿Value of pointer to member is not unique enough? Commented Nov 23 at 9:15
  • @wohlstad: I need to put the result of getUniqueId() into a std::unordered_map<> (as a key), to associate some data with the method. So, getUniqueId() could return pretty much anything that is hashable. Commented Nov 23 at 10:18
  • @user7860670: (see the comment above) the problem with that is that I cannot use a pointer to member function as a key in std::unordered_map. Commented Nov 23 at 10:19
  • sole library + uuid generation will work. Commented Nov 23 at 16:40

2 Answers 2

7

Here is an option that worked in C++17 for me.


#include <iostream>
#include <typeinfo>

class MyClass {
    template<auto F>
    struct FunctionTag {};

    template<auto F>
    static std::size_t getUniqueId() {
        return typeid(FunctionTag<F>).hash_code();
    }

public:
    void method1(bool) {
        std::cout << "My ID is "
                  << getUniqueId<&MyClass::method1>()
                  << '\n';
    }

    void method2(int) {
        std::cout << "My ID is "
                  << getUniqueId<&MyClass::method2>()
                  << '\n';
    }

    void method3(bool) {
        std::cout << "My ID is "
                  << getUniqueId<&MyClass::method3>()
                  << '\n';
    }
};
int main() { MyClass a; a.method1(true); a.method2(1); a.method3(true); }
New contributor
joost jasper is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
Sign up to request clarification or add additional context in comments.

Comments

5

Pointer to member function can be used as a template argument.

We are able to obtain the "pretty name" of the current function. This can be done via std::source_location since C++20, and compiler defined macros like __PRETTY_FUNCTION__ (for GCC and LLVM) and __FUNCSIG__ (for MSVC) before C++20. Template arguments, if any, will be contained in the "pretty name".

These lead us to a solution like the following:

// This demonstrates that both the member function's fully qualified name and its type can be obtained.
// You can adjust the contents of this string or encode it to a unique integer on your own.
template <auto MemFunPtr, typename Ty = decltype(MemFunPtr)>
constexpr auto getUniqueId() {
#if __cplusplus > 201703L
    return std::string_view{std::source_location::current().function_name()};
#else
    return std::string_view{__PRETTY_FUNCTION__};
#endif
}

Usage:

constexpr auto id = getUniqueId<&Class::method1>();

Demo on compiler explorer: https://godbolt.org/z/Kxa94jT1o

3 Comments

That's very nice! I actually got very close to this, but I gave up, because instead of function_name() I was calling the line() method, and that returns the line within the template code (that is, it's returning the same value for all three methods). But is this solution guaranteed to work with all compilers? Couldn't some compiler implement function_name() without inserting the name of the calling method?
I think the key difference between yours (calling line()) and mine is that I'm wrapping the information needed as a template argument. I'm calling function_name() on getUniqueId(), not on the caller of it. I also once tried calling source_location::current() as default arg of template param, but the result was not satisfactory (just as you said, it is always the location of the template) and you can see this question that I asked.
Is this solution guaranteed to work with all compilers? This is implementation-defined of course, and perhaps we can consult the compilers' docs. But AFAIK it is not uncommon to use this trick that depends on the inclusion of template args in the "pretty function" stuff. An example. I think at least you can do some checking there in case this gets broken when changing compilers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.