Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Frequently Asked Questions

1. Why can't I compare boost::function objects with operator== or operator!=?
2. I see void pointers; is this [mess] type safe?
3. Why are there workarounds for void returns? C++ allows them!
4. Why (function) cloning?
5. How much overhead does a call through boost::function incur?

1.

Why can't I compare boost::function objects with operator== or operator!=?

Comparison between boost::function objects cannot be implemented "well", and therefore will not be implemented. The typical semantics requested for f == g given boost::function objects f and g are:

  • If f and g store function objects of the same type, use that type's operator== to compare them.
  • If f and g store function objects of different types, return false.

The problem occurs when the type of the function objects stored by both f and g doesn't have an operator==: we would like the expression f == g to fail to compile, as occurs with, e.g., the standard containers. However, this is not implementable for boost::function because it necessarily "erases" some type information after it has been assigned a function object, so it cannot try to call operator== later: it must either find a way to call operator== now, or it will never be able to call it later. Note, for instance, what happens if you try to put a float value into a boost::function object: you will get an error at the assignment operator or constructor, not in operator(), because the function-call expression must be bound in the constructor or assignment operator.

The most promising approach is to find a method of determining if operator== can be called for a particular type, and then supporting it only when it is available; in other situations, an exception would be thrown. However, to date there is no known way to detect if an arbitrary operator expression f == g is suitably defined. The best solution known has the following undesirable qualities:

  1. Fails at compile-time for objects where operator== is not accessible (e.g., because it is private).
  2. Fails at compile-time if calling operator== is ambiguous.
  3. Appears to be correct if the operator== declaration is correct, even though operator== may not compile.

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.

The other option is to place the burden on users that want to use operator==, e.g., by providing an is_equality_comparable trait they may specialize. This is a workable solution, but is dangerous in practice, because forgetting to specialize the trait will result in unexpected exceptions being thrown from boost::function's operator==. This essentially negates the usefulness of operator== in the context in which it is most desired: multitarget callbacks. The Signals library has a way around this.

2.

I see void pointers; is this [mess] type safe?

Yes, boost::function is type safe even though it uses void pointers and pointers to functions returning void and taking no arguments. Essentially, all type information is encoded in the functions that manage and invoke function pointers and function objects. Only these functions are instantiated with the exact type that is pointed to by the void pointer or pointer to void function. The reason that both are required is that one may cast between void pointers and object pointers safely or between different types of function pointers (provided you don't invoke a function pointer with the wrong type).

3.

Why are there workarounds for void returns? C++ allows them!

Void returns are permitted by the C++ standard, as in this code snippet:

void f();
void g() { return f(); }

This is a valid usage of boost::function because void returns are not used. With void returns, we would attempting to compile ill-formed code similar to:

int f();
void g() { return f(); }

In essence, not using void returns allows boost::function to swallow a return value. This is consistent with allowing the user to assign and invoke functions and function objects with parameters that don't exactly match.

4.

Why (function) cloning?

In November and December of 2000, the issue of cloning vs. reference counting was debated at length and it was decided that cloning gave more predictable semantics. I won't rehash the discussion here, but if it cloning is incorrect for a particular application a reference-counting allocator could be used.

5.

How much overhead does a call through boost::function incur?

The cost of boost::function can be reasonably consistently measured at around 20ns +/- 10 ns on a modern >2GHz platform versus directly inlining the code.

However, the performance of your application may benefit from or be disadvantaged by boost::function depending on how your C++ optimiser optimises. Similar to a standard function pointer, differences of order of 10% have been noted to the benefit or disadvantage of using boost::function to call a function that contains a tight loop depending on your compilation circumstances.

[Answer provided by Matt Hurd. See http://article.gmane.org/gmane.comp.lib.boost.devel/33278]


PrevUpHomeNext