Comparing std::functions for equality?

前端 未结 7 479
自闭症患者
自闭症患者 2020-12-03 04:47

How can I compare two C++11 std::functions with operator==, and return true if both of said functions refer to the same f

相关标签:
7条回答
  • 2020-12-03 04:50

    If the std::function<T(U...)> f is a member function,the fnPointer will be null.

    0 讨论(0)
  • 2020-12-03 04:56

    You can actually get it to work with .target:

    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;
    }
    
    if (getAddress(f) == getAddress(g)) {...}
    

    (Ref: C++ trying to get function address from a std::function)

    0 讨论(0)
  • 2020-12-03 04:57

    operator== for std::function compares a std::function with a null pointer, as far as I can tell the standard does not provide any details as to why.

    Although, this boost FAQ entry, Why can't I compare boost::function objects with operator== or operator!=? provides a rationale and as far as I can tell should be applicable to std::function as well. Quoting the FAQ:

    Comparison between boost::function objects cannot be implemented "well", and therefore will not be implemented. [...]

    it then outlines requested solutions similar to Preet's and goes on to say:

    The problem occurs when the type of the function objects stored by both f and g doesn't have an operator==[...]

    and explains why this has to has to be dealt with in either the assignment operator or constructor and then goes on to say:

    All of these problems translate into failures in the boost::function constructors or assignment operator, even if the user never invokes operator==. We can't do that to users.

    Update

    Found a standards rationale in Accessing the target of a tr1::function object, which is pretty old but is consistent with the boost FAQ and says:

    operator== is unimplementable for tr1::function within the C++ language, because we do not have a reliable way to detect if a given type T is Equality Comparable without user assistance.

    0 讨论(0)
  • 2020-12-03 04:58

    Well, if you are not afraid of hacks, you can do something like this:

    // Simple function means no std::bind was used
    bool IsSimpleFunction(std::function<void(Args...)> function)
    {
        typedef void(functionType)(Args...);
        functionType** functionPointer = function.template target<functionType*>();
        return functionPointer != NULL;
    }
    
    bool AreEqual(std::function<void(Args...)> left, std::function<void(Args...)> right)
    {
        const int size = sizeof(std::function<void(Args...)>);
        std::byte leftArray[size] = { {(std::byte)0} };
        std::byte rightArray[size] = { {(std::byte)0} };
        std::byte* leftByte = (std::byte*) new (&leftArray) std::function<void(Args...)>(left);
        std::byte* rightByte = (std::byte*) new (&rightArray) std::function<void(Args...)>(right);
    
        // PrintFunctionsBytes(leftByte, rightByte, size);
    
        // Here the HACK starts
        // By resetting certain values we are able to compare functions correctly
        // When values are reset it has the same effect as when these values are ignored
        bool isSimpleFunction = IsSimpleFunction(left);
        if (!isSimpleFunction)
        {
            ResetAt(leftArray, rightArray, 16);
        }
        ResetAt(leftArray, rightArray, 56);
        ResetAt(leftArray, rightArray, 57);
        // Here the HACK ends
    
        for (int i = 0; i < size; i++, leftByte++, rightByte++)
        {
            if (*leftByte != *rightByte)
            {
                return false;
            }
        }
        return true;
    }
    
    void ResetAt(std::byte* leftArray, std::byte* rightArray, int i)
    {
        leftArray[i] = (std::byte)0;
        rightArray[i] = (std::byte)0;
    }
    
    // Only for debug
    void PrintFunctionsBytes(std::byte* leftFirstByte, std::byte* rightFirstByte, unsigned long long size)
    {
        std::vector<std::byte> leftVector(leftFirstByte, leftFirstByte + size);
        std::vector<std::byte> rightVector(rightFirstByte, rightFirstByte + size);
        std::cout << "Left: ";
        for (int i = 0; i < size; i++)
        {
            std::cout << i << ':' << (int)leftVector[i] << std::endl;
        }
        std::cout << "Right: ";
        for (int i = 0; i < size; i++)
        {
            std::cout << i << ':' << (int)rightVector[i] << std::endl;
        }
    }
    

    This was tested in MSVC and on 64 bit release configuration. And it was working for simple functions and for std::bind converted to std::function.

    If you have different compiler or build configuration, you'll have to adjust ignored bytes for your environment.

    Complete example here: https://github.com/linksplatform/Delegates

    0 讨论(0)
  • 2020-12-03 04:58

    What about comparing two shared_ptr?

    using MessageFilter = std::function<void(const int msgID)>;
    
    static void onMessageReceived(const int msgID)
    {
        std::cout << "msg id => " << msgID << std::endl;
    }
    
    static void someFunc()
    {
        auto filter = std::make_shared<MessageFilter>(&onMessageReceived);
    
        if (filter && *filter)
        {
            (*filter)(1234);
        }
    }
    

    As you can see, 'filter' is a shared_ptr, so it is easy to compare with another.

    0 讨论(0)
  • 2020-12-03 05:09

    You could try comparing a and b first by comparing their .target_type() and if these target type ids are the same, then you can compare their .target() pointers. You can use a mismatching target type as an early out false.

    0 讨论(0)
提交回复
热议问题