Proper support of volatile qualifier of member functions in STL - c++

Improper support of volatile-qualified overloadings of member functions in STL prevents using of containers, smart pointers, etc in generic way. Say, I want to declare a wrapper class, that provides value semantics and allows incompleteness of underlying type:
#include <type_traits>
#include <utility>
#include <memory>
template< typename type >
struct recursive_wrapper
{
using value_type = type;
template< typename ...arguments >
recursive_wrapper(arguments &&... _arguments)
: storage_(std::make_unique< type >(std::forward< arguments >(_arguments)...))
{ ; }
operator type & () & noexcept
{
return *storage_;
}
operator type const & () const & noexcept
{
return *storage_;
}
operator type && () && noexcept
{
return std::move(*storage_);
}
operator type const && () const && noexcept
{
return std::move(*storage_);
}
operator volatile type & () volatile & noexcept
{
return *storage_;
}
operator volatile type const & () volatile const & noexcept
{
return *storage_;
}
operator volatile type && () volatile && noexcept
{
return std::move(*storage_);
}
operator volatile type const && () volatile const && noexcept
{
return std::move(*storage_);
}
private :
std::unique_ptr< type > storage_;
};
// file:main.cpp
#include <iostream>
#include <vector>
#include <cstdlib>
int
main()
{
struct A;
struct B { recursive_wrapper< A > a; };
struct A { std::vector< B > b; };
{ // basic usage
B b;
A & a = b.a; // OK
static_cast< void >(a);
}
// let's add cv-qualifiers
{
volatile B b;
volatile A & a = b.a; // error!
static_cast< void >(a);
}
return EXIT_SUCCESS;
}
Lack of appropriate volatile-qualified overloading of std::unqie_ptr::operator * () causes an error:
main.cpp:38:16: error: indirection requires pointer operand ('volatile std::unique_ptr<A>' invalid)
return *storage_;
^~~~~~~~~
main.cpp:83:30: note: in instantiation of member function 'recursive_wrapper<A>::operator volatile A &' requested here
volatile A & a = b.a;
^
1 error generated.
The same story WRT std::container::push_back(), size(), etc.
It totally prevents a using of objects of STL (not involving const_cast operator) in generic code, which uses volatile member-function qualifier.
What is a reason of such poor STL design decision? Why volatile member function qualifier not properly supported in STL?? Is volatile member function qualifier depricated?

It's a very good decision. It's because volatility would be outright wrong for most types.
Keep in mind that volatile on an object means the object's fields can mutate spontaneously.
Consider the following, and assume the system guarantees that, at any given instant, begin and end will point to the same memory block:
template<class T>
class vector
{
T *begin;
T *end;
vector(vector const volatile &other) : begin(other.begin), end(other.end) { ... }
};
It turns out vector::vector(vector const volatile &) is wrong, because it cannot ensure that begin and end are read simultaneously.
Consequently, the copy it creates might have a begin and end that are out of sync, even though the original was completely fine.
I think this should be enough to make you realize why volatile is barely used.
It simply isn't used for the same reason that you were probably expecting used (i.e. atomics).
Its use case is entirely different and uncommon, and it is not something you throw around on a whim the way you might with const.

"Poor design decision"? Not really. The keyword is inherited from C, but there is very little use for it today. It's not actively deprecated, but its main use is in simple cases. Memory-mapped hardware would be a good example. But there won't be a memory-mapped std::deque<> so there's little point in STL support for that.

Related

Forwarding cv-ref-qualifier for member functions

If there are no another overloadings (say, f(T &) or f(volatile T &&)) of a (member) function template template< typename T > f(T &&);, then T && is so-called forwarding reference, and T is either U, or U & for some cv-qualified type U. But for cv-ref-qualifiers of member functions there is no such a rule. In struct S { void f() && { ; } }; a S::f() has always rvalue-reference qualifier.
In generic code it would be very useful to avoid a definition of 4 (or even 8, if we also consider volatile qualifier) overloadings of some member function, in cases if all of them doing generally the same thing.
Another problem that arises in this way, it is impossibility to define an effective cv-ref-qualifier of *this in a particular sense. Following code not allows one to determine whether the ref-qualifier of a member function operator () is && of &.
#include <type_traits>
#include <utility>
#include <iostream>
#include <cstdlib>
#define P \
{ \
using this_ref = decltype((*this)); \
using this_type = std::remove_reference_t< this_ref >; \
std::cout << qual() << ' ' \
<< (std::is_volatile< this_type >{} ? "volatile " : "") \
<< (std::is_const< this_type >{} ? "const " : "") \
<< (std::is_lvalue_reference< this_ref >{} ? "&" : "&&") \
<< std::endl; \
}
struct F
{
constexpr int qual() & { return 0; }
constexpr int qual() const & { return 1; }
constexpr int qual() && { return 2; }
constexpr int qual() const && { return 3; }
constexpr int qual() volatile & { return 4; }
constexpr int qual() volatile const & { return 5; }
constexpr int qual() volatile && { return 6; }
constexpr int qual() volatile const && { return 7; }
void operator () () & P
void operator () () const & P
void operator () () && P
void operator () () const && P
void operator () () volatile & P
void operator () () volatile const & P
void operator () () volatile && P
void operator () () volatile const && P
};
int
main()
{
{
F v;
F const c{};
v();
c();
std::move(v)();
std::move(c)();
}
{
volatile F v;
volatile F const c{};
v();
c();
std::move(v)();
std::move(c)();
}
return EXIT_SUCCESS;
}
But it would be very nice, if there was above syntax. I.e. decltype((*this)) denote exact cv-ref-qualified type of *this. It would not be a breaking-change to introduce such a syntax into coming version of the C++ standard at my mind. But && as forwarding cv-ref-qualifier is (and it looks like an omission of the committee (namely, core language working group)).
Another sequence is possible to denote both the member function cv-ref-qualifier and cv-ref-qualified type of *this into its body: auto &&, decltype(&&) etc.
Is there a proposal regarding this issue, prepared for use in C++17?
Yes, there are such proposals.
Background:
Since we already have forwarding references in template functions, you could simply turn your member function into a template friend function (and protect it via enable_if from being used with any other class than F, if required).
Now, maybe you want your really, really want to use your function as a member function, because you really, really like that syntax so much better.
The proposals:
Look up unified call syntax proposals, for instance: n4174
If something like that is accepted, it you will be able to use free functions like member functions of the first argument. This would cover the example code you linked in your first comment. Admittedly, it would not cover operator(), but I consider that a minor nuisance compared to writing 8 overloads :-)

Why I'm not able to prevent the undesirable C-style cast to compile?

There is an undesirable C-style cast that I'm not able to prevent to compile. The undesirable cast performs a C-style cast from an object of some class to a non-const reference of some other class. The classes are unrelated. In the same time I like to support the C-style cast from an object of the same class to the const reference. I'm providing a public conversion operator to support the desirable cast. It seems that it is impossible to prevent the undesirable cast in this case.
The cast to non-const reference fails to build ("Sandbox::B::operator Sandbox::A &()" (declared at line 30) is inaccessible*), unfortunately cast to const reference either fails (error: more than one conversion function from "Sandbox::B" to "const Sandbox::A" applies:
function "Sandbox::B::operator const Sandbox::A &()"
function "Sandbox::B::operator Sandbox::A &()"):
#include <iostream>
#include <string>
#include <cstdlib>
namespace Sandbox {
class A {
public:
A (int i) : _x (i) { }
private:
int _x;
};
class B {
public:
B (const char* m) : _m (m), _a (std::atoi (m)) { }
/*
* This one shall be supported.
*/
operator const A& () {
return _a;
}
private:
/*
* This one shall be not supported.
* If this one is disabled both desired and undesired conversions pass the compilation.
*/
operator A& ();
const std::string _m;
const A _a;
};
}
int main () {
Sandbox::A a (1973);
Sandbox::B b ("1984");
/*
* This is the undesirable cast and it shall fail to compile.
*/
(Sandbox::A&)b;
/*
* This is the desirable cast and it shall pass the compilation.
*/
(const Sandbox::A&)b;
return 0;
}
If I'm disabling operator operator A& () both desired and undesired conversions are build.
I'm using gcc, icc and MSVC compiles.
I cannot control the client code and prevent there use of C-style cast.
This should do the trick (tested on clang3.5):
#include <iostream>
#include <string>
#include <cstdlib>
namespace Sandbox {
class A {
public:
A (int i) : _x (i) { }
void fun()
{
std::cout << "action" << std::endl;
}
private:
int _x;
};
class B {
public:
B (const char* m) : _m (m), _a (std::atoi (m)) { }
/*
* This one shall be supported.
*/
template<typename T, typename Enable = typename std::enable_if<std::is_same<T, A>::value, A>::type>
operator const T& ()
{
return _a;
}
/*
* This one shall be not supported.
* If this one is disabled both desired and undesired conversions pass the compilation.
*/
private:
template<typename T, typename Enable = typename std::enable_if<std::is_same<T, A>::value, A>::type>
operator T& ();
const std::string _m;
const A _a;
};
}
int main () {
Sandbox::A a (1973);
Sandbox::B b ("1984");
/*
* This is the undesirable cast and it shall fail to compile.
*/
(Sandbox::A&)b;
/*
* This is the desirable cast and it shall pass the compilation.
*/
(const Sandbox::A&)b;
return 0;
}
As for why your version doesn't do what you want, it is related to the rules of the C-Style cast:
When the C-style cast expression is encountered, the compiler attempts
the following cast expressions, in this order:
a) const_cast(expression)
b) static_cast(expression),
with extensions: pointer or reference to a derived class is
additionally allowed to be cast to pointer or reference to unambiguous
base class (and vice versa) even if the base class is inaccessible
(that is, this cast ignores the private inheritance specifier). Same
applies to casting pointer to member to pointer to member of
unambigous non-virtual base
c) static_cast (with extensions) followed
by const_cast
d) reinterpret_cast(expression)
e)
reinterpret_cast followed by const_cast
The first choice that
satisfies the requirements of the respective cast operator is
selected, even if it cannot be compiled
Disclaimer: This explanation is based on guesses mostly, there are multiple steps and complex rules so i'm not sure everything really works as i think i've understood it but here you go.
Since you cast to a reference, reinterpret_cast will always works based on its rules of type aliasing, so the only way to make that C-Style cast fail is to make a static_cast on that type unambiguously produce an error. Unfortunately the conversion rules doesn't seem to consider a user defined conversion to const type to be a better match than a user defined conversion to a non cv-qualified type, they are both on the same level even if the static_cast target type is const qualified. Whereas with templates, SFINAE and parameter deduction kicks in and with some magic compiler powder extracted from a mountain dragon, it works. (yeah this step is a little more mysterious to me too).

Implicit conversion from user-defined type to primitive type in C++

I am able to find plenty of information about implicit conversion from, say, an int to a user defined type. i.e. if a constructor takes an int as its parameter and is not prefaced by "explicit" then implicit conversions can occur.
What if I want my class to implicitly convert to an int?
For example, what function needs to be added either inside or outside of SimpleClass such that the main function will compile and output "1" to the console? (see comments)
#include <iostream>
class SimpleClass
{
private:
int m_int;
public:
SimpleClass(int value)
: m_int(value) {}
};
int main(int argc, const char * argv[])
{
SimpleClass simpleclass(1);
int i = simpleclass; // does not complile
std::cout << i << std::endl; // should output "1" to the console
return 0;
}
Implicit conversions can be defined in two ways:
non-explicit single-argument constructor.
non-explicit conversion function (a.k.a. conversion operator), N3337 12.3.2
The later allows defining conversion from class type to primitive type. Just add
class SimpleClass {
// ...
operator int() const;
};
SimpleClass::operator int() const
{
return m_int;
}
The technical.
Conversion to (almost) any type T can be performed by an operator T member function.
It is by default invoked implicitly, and if you declare it const it can also be invoked on a const object.
Thus:
struct MyType
{
operator int() const { return 1; }
};
Problems…
Having a an implicit conversion to basic type allows free play for all the built-in operators, including
Arithmetic operators.
Boolean operators.
Relational operators.
So you better make sure that all this works the way you want.
And that can be a lot of work!
There are also potential problems with overload resolution for calls involving instances of your type.
In short, implicit conversion to int, or pointer, or any built-in type, usually costs more than it's worth.
An exception where it can be worthwhile is when a class is used a lot, in a library.
What you can do about it.
Avoid implicit conversion, but do offer explicit conversion.
The best general explicit conversion is, IMHO, a named member function.
An alternative is an operator T prefixed with the keyword explicit, which is supported for this use in C++11 and later (in C++03 it could only be used on constructors).
If you want output via << to behave as if an implicit conversion is performed, then just define an operator<<.
And similarly for other situations where an implicit conversion would appear to be a general solution: just define what's appropriate for that specific situation, and avoid introducing a general implicit conversion.
To provide implicit conversion to a built-in type and yet avoid the “free for all” for built-in operators, you can use a templatized type conversion, e.g. like this:
#include <iostream>
template< class A, class B > struct Is_t_;
template< class Type > struct Is_t_<Type, Type> { using T = void; };
template< class A, class B >
using If_is_ = typename Is_t_<A, B>::T;
struct Bad_string
{
operator const char* () const { return "666!"; }
Bad_string( char const* = 0 ) {}
};
auto operator==( Bad_string const&, Bad_string const& )
-> bool
{ return true; }
struct Good_string
{
template< class Type, class Enabled_ = If_is_<const char*, Type>>
operator Type() const { return "42 :)"; }
Good_string( char const* = 0 ) {}
};
auto operator==( Good_string const&, Good_string const& )
-> bool
{ return true; }
#if defined( DO_GOOD )
using String = Good_string;
#elif defined( DO_BAD )
using String = Bad_string;
#else
# error "Define either DO_GOOD or DO_BAD, please."
#endif
auto main() -> int
{
String a, b;
(void) (a == "alfalfa"); // Errs for Bad_string
(void) (a + 1); // Compiles for Bad_string.
}
Ironically, when DO_GOOD is defined this code crashes the Visual C++ 2015 update 1 compiler, a so called “ICE” (Internal Compiler Error).
A workaround for that compiler is to instead define If_is_ as
template< class A, class B >
using If_is_ = std::enable_if_t< std::is_same<A, B>::value >;
To allow your class to be converted to an int, implement
operator int() const
{
return m_int;
}

What are the use cases for having a function return by const value for non-builtin type?

Recently I have read that it makes sense when returning by value from a function to qualify the return type const for non-builtin types, e.g.:
const Result operation() {
//..do something..
return Result(..);
}
I am struggling to understand the benefits of this, once the object has been returned surely it's the callers choice to decide if the returned object should be const?
Basically, there's a slight language problem here.
std::string func() {
return "hai";
}
func().push_back('c'); // Perfectly valid, yet non-sensical
Returning const rvalues is an attempt to prevent such behaviour. However, in reality, it does way more harm than good, because now that rvalue references are here, you're just going to prevent move semantics, which sucks, and the above behaviour will probably be prevented by the judicious use of rvalue and lvalue *this overloading. Plus, you'd have to be a bit of a moron to do this anyway.
It is occasionally useful. See this example:
class I
{
public:
I(int i) : value(i) {}
void set(int i) { value = i; }
I operator+(const I& rhs) { return I(value + rhs.value); }
I& operator=(const I& rhs) { value = rhs.value; return *this; }
private:
int value;
};
int main()
{
I a(2), b(3);
(a + b) = 2; // ???
return 0;
}
Note that the value returned by operator+ would normally be considered a temporary. But it's clearly being modified. That's not exactly desired.
If you declare the return type of operator+ as const I, this will fail to compile.
There is no benefit when returning by value. It doesn't make sense.
The only difference is that it prevents people from using it as an lvalue:
class Foo
{
void bar();
};
const Foo foo();
int main()
{
foo().bar(); // Invalid
}
Last year I've discovered another surprising usecase while working on a two-way C++-to-JavaScript bindings.
It requires a combination of following conditions:
You have a copyable and movable class Base.
You have a non-copyable non-movable class Derived deriving from Base.
You really, really do not want an instance of Base inside Derived to be movable as well.
You, however, really want slicing to work for whatever reason.
All classes are actually templates and you want to use template type deduction, so you cannot really use Derived::operator const Base&() or similar tricks instead of public inheritance.
#include <cassert>
#include <iostream>
#include <string>
#include <utility>
// Simple class which can be copied and moved.
template<typename T>
struct Base {
std::string data;
};
template<typename T>
struct Derived : Base<T> {
// Complex class which derives from Base<T> so that type deduction works
// in function calls below. This class also wants to be non-copyable
// and non-movable, so we disable copy and move.
Derived() : Base<T>{"Hello World"} {}
~Derived() {
// As no move is permitted, `data` should be left untouched, right?
assert(this->data == "Hello World");
}
Derived(const Derived&) = delete;
Derived(Derived&&) = delete;
Derived& operator=(const Derived&) = delete;
Derived& operator=(Derived&&) = delete;
};
// assertion fails when the `const` below is commented, wow!
/*const*/ auto create_derived() { return Derived<int>{}; }
// Next two functions hold reference to Base<T>/Derived<T>, so there
// are definitely no copies or moves when they get `create_derived()`
// as a parameter. Temporary materializations only.
template<typename T>
void good_use_1(const Base<T> &) { std::cout << "good_use_1 runs" << std::endl; }
template<typename T>
void good_use_2(const Derived<T> &) { std::cout << "good_use_2 runs" << std::endl; }
// This function actually takes ownership of its argument. If the argument
// was a temporary Derived<T>(), move-slicing happens: Base<T>(Base<T>&&) is invoked,
// modifying Derived<T>::data.
template<typename T>
void oops_use(Base<T>) { std::cout << "bad_use runs" << std::endl; }
int main() {
good_use_1(create_derived());
good_use_2(create_derived());
oops_use(create_derived());
}
The fact that I did not specify the type argument for oops_use<> means that the compiler should be able to deduce it from argument's type, hence the requirement that Base<T> is actually a real base of Derived<T>.
An implicit conversion should happen when calling oops_use(Base<T>). For that, create_derived()'s result is materialized into a temporary Derived<T> value, which is then moved into oops_use's argument by Base<T>(Base<T>&&) move constructor. Hence, the materialized temporary is now moved-from, and the assertion fails.
We cannot delete that move constructor, because it will make Base<T> non-movable. And we cannot really prevent Base<T>&& from binding to Derived<T>&& (unless we explicitly delete Base<T>(Derived<T>&&), which should be done for all derived classes).
So, the only resolution without Base modification here is to make create_derived() return const Derived<T>, so that oops_use's argument's constructor cannot move from the materialized temporary.
I like this example because not only it compiles both with and without const without any undefined behaviour, it behaves differently with and without const, and the correct behavior actually happens with const only.

Why is std::function not equality comparable?

This question also applies to boost::function and std::tr1::function.
std::function is not equality comparable:
#include <functional>
void foo() { }
int main() {
std::function<void()> f(foo), g(foo);
bool are_equal(f == g); // Error: f and g are not equality comparable
}
In C++11, the operator== and operator!= overloads just don't exist. In an early C++11 draft, the overloads were declared as deleted with the comment (N3092 §20.8.14.2):
// deleted overloads close possible hole in the type system
It does not say what the "possible hole in the type system" is. In TR1 and Boost, the overloads are declared but not defined. The TR1 specification comments (N1836 §3.7.2.6):
These member functions shall be left undefined.
[Note: the boolean-like conversion opens a loophole whereby two function instances can be compared via == or !=. These undefined void operators close the loophole and ensure a compile-time error. —end note]
My understanding of the "loophole" is that if we have a bool conversion function, that conversion may be used in equality comparisons (and in other circumstances):
struct S {
operator bool() { return false; }
};
int main() {
S a, b;
bool are_equal(a == b); // Uses operator bool on a and b! Oh no!
}
I was under the impression that the safe-bool idiom in C++03 and the use of an explicit conversion function in C++11 was used to avoid this "loophole." Boost and TR1 both use the safe-bool idiom in function and C++11 makes the bool conversion function explicit.
As an example of a class that has both, std::shared_ptr both has an explicit bool conversion function and is equality comparable.
Why is std::function not equality comparable? What is the "possible hole in the type system?" How is it different from std::shared_ptr?
Why is std::function not equality comparable?
std::function is a wrapper for arbitrary callable types, so in order to implement equality comparison at all, you'd have to require that all callable types be equality-comparible, placing a burden on anyone implementing a function object. Even then, you'd get a narrow concept of equality, as equivalent functions would compare unequal if (for example) they were constructed by binding arguments in a different order. I believe it's impossible to test for equivalence in the general case.
What is the "possible hole in the type system?"
I would guess this means it's easier to delete the operators, and know for certain that using them will never give valid code, than to prove there's no possibility of unwanted implicit conversions occurring in some previously undiscovered corner case.
How is it different from std::shared_ptr?
std::shared_ptr has well-defined equality semantics; two pointers are equal if and only if they are either both empty, or both non-empty and pointing to the same object.
I may be wrong, but I think that equality of std::function objects is unfortunately not solvable in the generic sense. For example:
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <cstdio>
void f() {
printf("hello\n");
}
int main() {
boost::function<void()> f1 = f;
boost::function<void()> f2 = boost::bind(f);
f1();
f2();
}
are f1 and f2 equal? What if I add an arbitrary number of function objects which simply wrap each other in various ways which eventually boils down to a call to f... still equal?
Why is std::function not equality comparable?
I think main reason is that if it were, then it couldn't be used with non equality comparable types, even if equality comparison is never performed.
I.e. code that performs comparison should be instantiated early - at the time when a callable object is stored into std::function, for instance in one of the constructors or assignment operators.
Such a limitation would greatly narrow the scope of application, and obviously not be acceptable for a "general-purpose polymorphic function wrapper".
It is important to note that it is possible to compare a boost::function with a callable object (but not with another boost::function)
Function object wrappers can be compared via == or != against any function object that can be stored within the wrapper.
This is possible, because function that performs such comparison is instantiated at point of comparison, based on known operand types.
Moreover, std::function has a target template member function, which can be used to perform similar comparison. In fact boost::function's comparison operators are implemented in terms of target member function.
So, there are no technical barriers which block implementation of function_comparable.
Among answers there is common "impossible in general" pattern:
Even then, you'd get a narrow concept of equality, as equivalent functions would compare unequal if (for example) they were constructed by binding arguments in a different order. I believe it's impossible to test for equivalence in the general case.
I may be wrong, but I think that equality is of std::function objects is unfortunately not solvable in the generic sense.
Because the equivalence of Turing machines is undecidable. Given two different function objects, you cannot possibly determine if they compute the same function or not. [That answer was deleted]
I completely disagree with this: it is not the job of std::function to perform comparison itself; its job is just to redirect request to comparison to underlying objects - that's all.
If underlying object type does not define comparison - it will be a compilation error; in any case, std::function is not required to deduce a comparison algorithm.
If the underlying object type defines comparison, but which works wrongly, or has some unusual semantic - it is not the problem of std::function itself either, but it is problem of the underlying type.
It is possible to implement function_comparable based on std::function.
Here is a 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 a nice property - function_comparable can be compared against std::function too.
For instance, let's say we have vector of std::functions, and we want to give users register_callback and unregister_callback functions. Use of function_comparable is required only for unregister_callback parameter:
void register_callback(std::function<function_signature> callback);
void unregister_callback(function_comparable<function_signature> callback);
Live demo at Ideone
Source code of 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();
}
Output is:
func1
func2, data=1
func2, data=2
func3
________________
func1
func2, data=1
func3
________________
func2, data=1
func3
________________
P.S. It seems that with help of std::type_index, it is possible to implement something similar to function_comparable class, which also supports ordering (i.e. std::less) or even hashing. Not only ordering between different types, but also ordering within same type (this requires support from types, like LessThanComparable).
According to http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#1240:
The leading comment here is part of
the history of std::function, which
was introduced with N1402. During that
time no explicit conversion functions
existed, and the "safe-bool" idiom
(based on pointers-to-member) was a
popular technique. The only
disadvantage of this idiom was that
given two objects f1 and f2 of type
std::function, the expression
f1 == f2;
was well-formed, just because the
built-in operator== for pointer to
member was considered after a single
user-defined conversion. To fix this,
an overload set of undefined
comparison functions was added, such
that overload resolution would prefer
those ending up in a linkage error.
The new language facility of deleted
functions provided a much better
diagnostic mechanism to fix this
issue.
In C++11, the deleted functions are considered superfluous with the introduction of explicit conversion operators, so they will probably be removed for C++11.
The central point of this issue is,
that with the replacement of the
safe-bool idiom by explicit conversion
to bool, the original "hole in the type
system" does no longer exist and
therefore the comment is wrong and the
superfluous function definitions
should be removed as well.
As for why you can't compare std::function objects, it's probably because they can possibly hold global/static functions, member functions, functors, etc, and to do that std::function "erases" some information about the underlying type. Implementing an equality operator would probably not be feasible because of that.
Actually, you can compare targets. It may work depends of what you want from comparison.
Here the code with inequality, but you can see how it works:
template <class Function>
struct Comparator
{
bool operator()(const Function& f1, const Function& f2) const
{
auto ptr1 = f1.target<Function>();
auto ptr2 = f2.target<Function>();
return ptr1 < ptr2;
}
};
typedef function<void(void)> Function;
set<Function, Comparator<Function>> setOfFunc;
void f11() {}
int _tmain(int argc, _TCHAR* argv[])
{
cout << "was inserted - " << setOfFunc.insert(bind(&f11)).second << endl; // 1 - inserted
cout << "was inserted - " << setOfFunc.insert(f11).second << endl; // 0 - not inserted
cout << "# of deleted is " << setOfFunc.erase(f11) << endl;
return 0;
}
Ups, it is only valid since C++11.
How about trying something like the following, this works well for testing templates.
if (std::is_same<T1, T2>::value)
{
...
}
the least that could be done is if std::function saves the address of the function used for binding to a string and used string comparison instead.