views:

188

answers:

1

I was having a look at the "Function" class documentation in Boost, and stumbled across this:

boost::function<float (int x, int y)> f;

I must admit this syntax is highly confusing for me. How can this be legal C++ ?

Is there any trick under the hood ? Is this syntax documented anywhere?

+6  A: 

[Edit] This is an answer to the author's original, unedited question which was actually two questions.

I must admit this syntax is highly confusing for me. How can this be legal C++ ? :) Is there any trick under the hood ? Is this syntax documented anywhere ?

This is perfectly legal and it's actually not too complicated.

template <class T>
class C
{
public:
    T* function_pointer;
};

void fn(int x)
{
    cout << x << endl;
}

int main(int argc, char** argv)
{
    C<void (int x)> c;
    c.function_pointer = &fn;
    c.function_pointer(123); // outputs x
}

It's basically the same thing as doing:

typedef void Function(int);
C<Function> c;

This type is not just applicable C++, it's just as applicable in C (the actual type C is parameterized to). The template magic here is taking something like the Function typedef here and being able to detect the types of the return values and arguments. Explaining that would be too lengthy here and boost::function uses a lot of the function traits meta-templates in boost to retrieve that information. If you really want to spend the time to learn this, you should try to understand the boost implementation starting with boost::function_traits in Boost.Type Traits.

However, I want to address your general problem. I think you are trying too hard to simplify a couple of lines of perfectly acceptable code. There's nothing wrong with passing the arguments for your command subclasses through their parameterized subclass constructors. Is is really worth trying to involve typelists and boost::function-like solutions here, thereby increasing compile times and code complexity significantly, just for that?

If you want to reduce it further, just write an execute function that will execute any command subclass and add it to the undo stack and so on:

typedef boost::shared_ptr<Command> CommandPtr;

void execute(const CommandPtr& cmd)
{
    cmd->execute();
    // add command to undo stack or whatever else you want to do
}

// The client can simply write something like this:
execute(CommandPtr(new CmdAdd(some_value) );

I really think the trade-off of trying to make it any more complicated isn't worth it. The boost authors wanted to write an extremely general-purpose solution for boost::function which would be used by many people across many platforms and compilers. However, they didn't try to generalize a command system capable of executing functions with different signatures across a unified undo system (thereby requiring the state of these commands to be preserved even after one is initially finished calling them and be able to undo and re-execute them without re-specifying the original state data on subsequent executions). For that, your inheritance-based approach is most likely the best and most straightforward one.

`typedef float (int, int) Command`, I'm sorry to say, is not legal C. The correct C code would be `typedef float Command(int, int)`. `typedef` takes a regular declaration, and instead of defining the named object (ie, function or variable), it uses the name as alias for type type of the object.
Dale Hagglund
+1 Great answer
fingerprint211b
@Dale Apologies, you are correct and that's also illegal in C++. That was a hasty mistake when trying to explain what C was parameterized to with C<float (int, int)> c; It is the equivalent of writing:typedef float Command(int, int); C<Command> c. I edited it to correct the mistake.
@Dale i'm afraid i voted up your comment prematurely :) Notice that functions aren't objects.
Johannes Schaub - litb
@Johannes: I know that formally functions aren't object, but I was trying to distinguish between a run-time entity (a data object or a function) and a compile-type entity (the type declared by a typedef).
Dale Hagglund