Why is std :: function not equally comparable?
I think the main reason is that if that were the case, then it cannot be used with comparable types of equality, even if equality comparison is never performed.
those. the code that performs the comparison must be created earlier - at the moment when the called object is stored in std :: function, for example, in one of the constructors or assignment operators.
Such a limitation will significantly reduce the scope of application and, obviously, is unacceptable for a “universal polymorphic functional shell” .
It cannot be noticed that it is possible to compare boost :: function with the called object (but not with another boost :: function)
Shells of object objects can be compared using == or! = With any functional object that can be stored in the shell.
This is possible because the function that performs this comparison is instantly initialized at the comparison point, based on knowledge of the type of operand.
In addition, std :: function has a target member function , which can be used for similar comparisons. In fact, boost :: function comparison operators are implemented in terms of the target function.
Thus, there are no technical barriers that block the implementation of function_comparable .
Among the answers there is a general "impossible in general" pattern:
Even then you would get a narrow concept of equality, since equivalent functions would compare unequal ones if (for example) they were constructed by binding arguments in a different order. I believe that it is impossible to verify equivalence in the general case.
Perhaps I am mistaken, but I think that the equality of std :: function objects, unfortunately, is not decidable in the general sense.
Because the equivalence of turing machines is insoluble. Given two different functional objects, you cannot determine whether they calculate the same function or not. [This answer has been deleted]
I completely disagree with this: it’s not that std :: function does the comparison itself, this task is just to redirect the request for comparison with basic objects - that’s all.
If the type of the base object does not determine the comparison, in any case it will be a compilation error, std :: function is not required to determine the comparison algorithm.
If the type of the base object determines the comparison, but which does not work correctly or has some unusual semantics, this is also not a problem of std :: function itself, but it is a problem of the base type .. p>
You can implement function_comparable based on std :: function.
Here is the proof of concept:
template<typename Callback,typename Function> inline bool func_compare(const Function &lhs,const Function &rhs) { typedef typename conditional < is_function<Callback>::value, typename add_pointer<Callback>::type, Callback >::type request_type; if (const request_type *lhs_internal = lhs.template target<request_type>()) if (const request_type *rhs_internal = rhs.template target<request_type>()) return *rhs_internal == *lhs_internal; return false; } #if USE_VARIADIC_TEMPLATES #define FUNC_SIG_TYPES typename ...Args #define FUNC_SIG_TYPES_PASS Args... #else #define FUNC_SIG_TYPES typename function_signature #define FUNC_SIG_TYPES_PASS function_signature #endif template<FUNC_SIG_TYPES> struct function_comparable: function<FUNC_SIG_TYPES_PASS> { typedef function<FUNC_SIG_TYPES_PASS> Function; bool (*type_holder)(const Function &,const Function &); public: function_comparable() {} template<typename Func> function_comparable(Func f) : Function(f), type_holder(func_compare<Func,Function>) { } template<typename Func> function_comparable &operator=(Func f) { Function::operator=(f); type_holder=func_compare<Func,Function>; return *this; } friend bool operator==(const Function &lhs,const function_comparable &rhs) { return rhs.type_holder(lhs,rhs); } friend bool operator==(const function_comparable &lhs,const Function &rhs) { return rhs==lhs; } friend void swap(function_comparable &lhs,function_comparable &rhs)// noexcept { lhs.swap(rhs); lhs.type_holder.swap(rhs.type_holder); } };
There is some nice property - function_comparable can be compared with std :: function .
For example, let's say we have a std :: function vector , and we want to give the user register_callback and unregister_callback . The use of function_comparable is required only for the unregister_callback parameter:
void register_callback(std::function<function_signature> callback); void unregister_callback(function_comparable<function_signature> callback);
Live demo in Perfect
Download source package demo:
// Copyright Evgeny Panasyuk 2012. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include <type_traits> #include <functional> #include <algorithm> #include <stdexcept> #include <iostream> #include <typeinfo> #include <utility> #include <ostream> #include <vector> #include <string> using namespace std; // _____________________________Implementation__________________________________________ #define USE_VARIADIC_TEMPLATES 0 template<typename Callback,typename Function> inline bool func_compare(const Function &lhs,const Function &rhs) { typedef typename conditional < is_function<Callback>::value, typename add_pointer<Callback>::type, Callback >::type request_type; if (const request_type *lhs_internal = lhs.template target<request_type>()) if (const request_type *rhs_internal = rhs.template target<request_type>()) return *rhs_internal == *lhs_internal; return false; } #if USE_VARIADIC_TEMPLATES #define FUNC_SIG_TYPES typename ...Args #define FUNC_SIG_TYPES_PASS Args... #else #define FUNC_SIG_TYPES typename function_signature #define FUNC_SIG_TYPES_PASS function_signature #endif template<FUNC_SIG_TYPES> struct function_comparable: function<FUNC_SIG_TYPES_PASS> { typedef function<FUNC_SIG_TYPES_PASS> Function; bool (*type_holder)(const Function &,const Function &); public: function_comparable() {} template<typename Func> function_comparable(Func f) : Function(f), type_holder(func_compare<Func,Function>) { } template<typename Func> function_comparable &operator=(Func f) { Function::operator=(f); type_holder=func_compare<Func,Function>; return *this; } friend bool operator==(const Function &lhs,const function_comparable &rhs) { return rhs.type_holder(lhs,rhs); } friend bool operator==(const function_comparable &lhs,const Function &rhs) { return rhs==lhs; } // ... friend void swap(function_comparable &lhs,function_comparable &rhs)// noexcept { lhs.swap(rhs); lhs.type_holder.swap(rhs.type_holder); } }; // ________________________________Example______________________________________________ typedef void (function_signature)(); void func1() { cout << "func1" << endl; } void func3() { cout << "func3" << endl; } class func2 { int data; public: explicit func2(int n) : data(n) {} friend bool operator==(const func2 &lhs,const func2 &rhs) { return lhs.data==rhs.data; } void operator()() { cout << "func2, data=" << data << endl; } }; struct Caller { template<typename Func> void operator()(Func f) { f(); } }; class Callbacks { vector<function<function_signature>> v; public: void register_callback_comparator(function_comparable<function_signature> callback) { v.push_back(callback); } void register_callback(function<function_signature> callback) { v.push_back(callback); } void unregister_callback(function_comparable<function_signature> callback) { auto it=find(v.begin(),v.end(),callback); if(it!=v.end()) v.erase(it); else throw runtime_error("not found"); } void call_all() { for_each(v.begin(),v.end(),Caller()); cout << string(16,'_') << endl; } }; int main() { Callbacks cb; function_comparable<function_signature> f; f=func1; cb.register_callback_comparator(f); cb.register_callback(func2(1)); cb.register_callback(func2(2)); cb.register_callback(func3); cb.call_all(); cb.unregister_callback(func2(2)); cb.call_all(); cb.unregister_callback(func1); cb.call_all(); }
Exit:
func1 func2, data=1 func2, data=2 func3 ________________ func1 func2, data=1 func3 ________________ func2, data=1 func3 ________________
PS It seems that using std :: type_index can be implemented similarly to the class function_comparable , which also supports ordering (i.e. smaller) or even hashing. But not only ordering between different types, but also ordering within the same type (this requires support for types such as LessThanComparable).