Wt and C++0x

  • Posted by koen
  • Friday, August 6, 2010 @ 08:58

We have been hearing "c++0x is just around the corner" for so many years now that it no longer sounds credible. However, chances are real that some of its goodness is already well supported by your compiler… and Wt !

While there is many things to like about this language revision, the feature I looked most forward to was lambdas. I need to say was, because it is there now, I’ve used it, and I was delighted !

Java can, since long, emulate lambdas (albeit in an overly verbose way) using anonymous inner classes, and this has proven handy (possibly a life-saver) for having a signal/slot implementation in Java, which lacks proper function delegates:

final WPushButton button = new WPushButton("Click me", this);

button.clicked().addListener(this, new Signal.Listener() {
    public void trigger() {
        button.setText("Thank you!");
    }
});

Lambdas make life a lot easier when you want to connect small functions to slots. It saves you work for two reasons:

  • you can define a small anonymous function at the single site where you want to use it, and thus you do not need to touch the header file and decide where to put the implementation, or read the implementation and wonder where it is being used.

  • you can enclose all your local variables in the function (including this) by copy, and thus do not need to add these as state fields to your class definition. In that way, important state for your widget will stand out!

The best of it: Wt supports lambdas for signal connections since 3.1.0, and you can use this already, at least if your compiler supports it. Most notably, you will need gcc 4.5 or later with -std=c++0x, or Microsoft Visual Studio 2010 for the following to work:

using namespace Wt;

WPushButton *button = new WPushButton("Click me", this);

button->clicked().connect([=] (const WMouseEvent& e)
{
    button->setText("Thank you!");
});

One slight annoyance is that you need to accept any signal’s arguments (such as a const WMouseEvent& in this case), even if you are not interested in the event details. This was not needed when using the old-style connect syntax since the signals tolerated connecting to functions with less arguments. We are unsure if this annoyance warrants a work-around.

Still, this feature alone makes it worth-while to consider adopting c++0x. Obviously we will not migrate Wt itself to c++0x for years to come, but you can already take advantage of it while using Wt — today !

Tags:
13 comments
  • Posted by dmitigr
  • 13 years ago
Great and informative post! Many thanks to Koen and OvermindDL1 for useful information on both C++0x lambdas and Phoenix!
  • Posted by qduaty
  • 13 years ago
using std::bind;
button->clicked().connect(bind([=]
{
button->setText("Thank you!");
}));
That's it. A minimalistic use of std::bind swallows the argument for you. Of course you can hide it in a template connect() method.
  • Posted by koen
  • 13 years ago
Hey, that works indeed, and I like it.
It doesn't work with boost::bind, is that because std::bind() offers more in this area or is it only because of the lambda which boost::bind doesn't grock?)
There is currently a template connect() method that matches any function object -- so I don't see how I can hide the std::bind in there ?
  • Posted by OvermindDL1
  • 13 years ago
I really do not like C++0x lambda's, they are restricted and monomorphic and so forth. Look at this example from your blog post:
using namespace Wt;
WPushButton *button = new WPushButton("Click me", this);
button->clicked().connect([=] (const WMouseEvent& e)
{
button->setText("Thank you!");
});

Now compare that to a Boost.Phoenix version:
using namespace Wt;
using namespace boost.phoenix;
WPushButton *button = new WPushButton("Click me", this);
button->clicked().connect(bind(&WPushButton::setText, button, "Thank you!"));

Although if you were to *properly* phoenixe'ize that last line, it would look like this:
button->clicked().connect(setText(button, "Thank you!")); // maybe _setText or setText_
// or so to prevent ambiguity,
// and this will work with
// anything that has a setText member,
// polymorphic or not

Although this is not the best example of lambda's as it can also be performed by boost::bind or so, a better example is this (using C++0x lambda's):
using namespace Wt;
using namespace boost.phoenix;
WPushButton *button = new WPushButton("Click me", this);
bool someStateThatPersists = false; // might be true later,
// might not... this persists though
button->clicked().connect([&someStateThatPersists, button] (const WMouseEvent& e)
// not sure the syntax is perfectly correct in the [] part...)
{
if(someStateThatPersists)
{
button->setText("Thank you!");
}
else
{
button->setText("Why am I still false?!? Setting myself to true for next time");
someStateThatPersists = true;
}
});

Or in a proper phoenix way:
using namespace Wt;
using namespace boost.phoenix;
WPushButton *button = new WPushButton("Click me", this);
bool someStateThatPersists = false; // might be true later,
// might not... this persists though
button->clicked().connect(
if_(ref(someStateThatPersists))
[
setText(button, "ThankYou!") // if button does not compile,
// depending on how you made setText,
// you might need to replace it with
// val(button) instead,
// properly made though, this should work.
]
.else
[
setText(button, "Why am I still false?!? Setting myself to true for next time"),
ref(someStateThatPersists)=true
]);

And yes, that is proper C++, that is phoenix, completely self contained, needs to signature, it ignores any passed in parameters that it does not use, and it is fully capable of inlining (unlike
C++0x lambda's in current compilers). You can completely simplify and coalesce phoenix functions to simplify the syntax and add abilities, a Wt made using phoenix composition (especially the new capabilities in phoenix3) would be truly powerful while being more simple.
Do note, phoenix works fine in Wt, I use it just like the above. :p
  • Posted by OvermindDL1
  • 13 years ago
*Please* fix the blog comment parser? It is eating all my space and   is not working!
  • Posted by koen
  • 13 years ago
There you go. I've implemented <code> tags for inserting a block of code (although it currently isn't syntax highlighted).
  • Posted by anonymous
  • 13 years ago
I didn't like C++0x lambda: http://uint32t.blogspot.com/2009/05/using-c0x-lambda-to-replace-boost-bind.html
  • Posted by anonymous
  • 13 years ago
Nice, just as that article describes the vast limitation (for my uses anyway) of C++0x lamdas being that they are monomorphic, Boost.Phoenix, as stated, is polymorphic, works perfectly.
  • Posted by anonymous
  • 13 years ago
Yeah, my conclusion is that the lack of syntax to support polymorphic functions makes it useless for me.
  • Posted by OvermindDL1
  • 13 years ago
That was me, this site apparently does not keep me logged in, sorry...
  • Posted by koen
  • 13 years ago
Nice to see how boost::bind / c++0x lambda's can be used as Qt slot (sort of) -- I think however that some of the problems indicated in that post are bugs in the Intel compiler (given that it works as expected with gcc), rather than bugs in the lambda spec ? My experience is limited though.
  • Posted by anonymous
  • 13 years ago
One of them is, but it doesn't change the conclusion which is that you still need binding a la Boost Bind and Phoenix :(
  • Posted by anonymous
  • 13 years ago
Good work!

Contact us for more information
or a personalised quotation