I've seen a nice trick made by an boost Implementation, they somehow use the overloading of the () operator to evaluate an instance of the class boost::system::error_code to an bool value
class error_code
{
...
typedef void (*unspecified_bool_type)();
static void unspecified_bool_true() {}
operator unspecified_bool_type() const // true if error
{
return m_val == 0 ? 0 : unspecified_bool_true;
}
...
}
This results in the possibility to check for an error like this
...
boost::system::error_code err
some_boost_func(err);
if(err)
{
//handle error
}
....
So i keep asking myself.. what happend there?
This seems to somehow relate to the use of function pointers...
What happens if i call err
does this evaluate the function itself or the function pointer?
But how can a void (*unspecified_bool_type)();
function return a value in
return m_val == 0 ? 0 : unspecified_bool_true;
It really has little (or nothing) to do with core functionality of function pointers specifically. It is a trick that allows one to write a "safe" boolean-like conversion for the class.
When one wants some class to be usable under if
(and generally in logic contexts), one typically makes it convertible to bool
. As in
class Error {
public:
operator bool() const { /* whatever */ }
};
and now you can do
Error err;
...
if (err) // automatically intepreted as `if (err.operator bool())`
...
However, since bool
type is an integral type in C++, this might lead to undesirable consequences, when someone accidentally writes something like
int i = err;
or uses err
in an arithmetic expression and it quietly compiles.
For this reason, in many cases people prefer to introduce a conversion to pointer type, instead of conversion to bool
, as in
class Error {
public:
operator void *() const {
// Return null pointer for `false` and any non-null pointer for `true`
}
};
This is better since one can use it under if
, but one can't make the previous mistake with int
. I.e.
if (err) // automatically interpreted as `if (err.operator void *() != 0)`
...
will compile and work as intended, since the compiler will automatically convert err
object to pointer type.
However, such conversion will be automatically applied in pointer contexts as well (in addition to boolean contexts), meaning that one can still accidentally do
void *p = err;
or
free(err);
and it will quietly compile. This is also undesirable.
To make it more difficult to accidentally misuse such error class, it is a good idea to use some more "exotic" pointer type, like pointer to a function. This is exactly what you see in the code you quoted. The unspecified_bool_type
is used as a pointer-based pseudo-boolean type. Null value is returned for false
and pointer to a dummy unspecified_bool_true
function is returned for true
. The function unspecified_bool_true
is never called and never intended to be called. It only exists to reserve some unique pointer value to be used as true
return.
In some cases people take it one step further and use an even more "exotic" pointer type: pointer-to-class-member type. But for most applications pointer to function is "exotic" enough.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With