Saturday 21 July 2018

More on Casablanca/cpprestdsk - Design and Comparisons


After I finished my Casablanca (aka C++ REST SDK)* project for some time then, I noticed** a new Boost library: Boost.Beast***, a library implementing: "HTTP and WebSocket built on Boost.Asio in C++11". 

That sounded interesting so I had a quick look at it, and I even found (surprise, surprise) the design rationale and comparison with other HTTP libraries. Because of my previous involvement with Casablanca* I simply had to read it. So here is my take on Beasts's critique and some closing discussion of Casablanca's* design and how I see it.

My involvement with Casablanca*/C++ REST-SDK code...
Let us start with a lengthy citation from Beast's analysis of Casablanca*, where the author lists all the missing customization points of the library:
"It is clear from this declaration that the goal of the message model in this library is driven by its use-case (interacting with REST servers) and not to model HTTP messages generally. We note problems similar to the other declarations:
  • There are no compile-time customization points at all. The only customization is in the concurrency::streams::istream and concurrency::streams::ostream reference parameters. Presumably, these are abstract interfaces which may be subclassed by users to achieve custom behaviors. 
  • The extraction of the body is conflated with the asynchronous model. 
  • No way to define an allocator for the container used when extracting the body. 
  • A body can only be extracted once, limiting the use of this container when using a functional programming style. 
  • Setting the body requires either a vector or a concurrency::streams::istream. No user defined types are possible. 
  • The HTTP request container conflates HTTP response behavior (see the set_response_stream member). Again this is likely purpose-driven but the lack of separation of concerns limits this library to only the uses explicitly envisioned by the authors."
Then the author restates it in a more concise manner:
"The general theme of the HTTP message model in cpprestsdk is "no user definable customizations". There is no allocator support, and no separation of concerns. It is designed to perform a specific set of behaviors. In other words, it does not follow the open/closed principle.
Tasks in the Concurrency Runtime operate in a fashion similar to std::future, but with some improvements such as continuations which are not yet in the C++ standard. The costs of using a task based asynchronous interface instead of completion handlers is well documented: synchronization points along the call chain of composed task operations which cannot be optimized away. See: A Universal Model for Asynchronous Operations (Kohlhoff)."
Let me state my opinion bluntly: I didn't miss these customization features at all during my almost 3 years long involvement with Casablanca*! The framework just did its work as it should, we could use it without much hassle and it performed reasonably good under load on Windows - probably through its usage of completion ports (Boost.Asio is only used on Mac Os and Linux).

OK, there was a single time where one of the above concerns did cause problems, namely:
  • A body can only be extracted once, limiting the use of this container when using a functional programming style,
but I solved it by setting the data into the message back again. Mischief managed ๐Ÿ˜ˆ, work can be continued! You could say it's not very functional programming style, but we are not in the academy but in real life.

On the other side the Boost.Beast itself library seems a little hard to use, and more like a base for another libraries than a independent library on its own, as it is stated in the project's own FAQ:
"It is not the intention of the library to provide turn-key solutions for specific HTTP or WebSocket use-cases. Instead, it is a sensible protocol layering on top of Boost.Asio which retains the Boost.Asio memory management style and asynchronous model."
So OK, we must be aware of Casablanca's* limitations, but for the real REST use case they somehow carried no weight.

TL;DR: I cannot really relate to the critique stated in the comparison, for us Casablanca worked out pretty well in general and the product we build with it is alive and kicking!

Note: Here and here I found quite interesting results of a security review of Boost.Beast by Bishop Fox. They found vulnerabilities, and I don't want to even think about their analysis of Casablanca*! Fortunately our product was set up forca more or less trusted environment. But this could be maybe a material for a separate blog post.

--
* Casablanca is the old code name for Microsoft's C++ REST SDK and I'll use it in the rest of that post, because it rolls better off the tongue than cpprestsdk! Besides during the years I grew accustomed to it and even a little nostalgic...

** to be more specific, I noticed it through Meeting C++'s library reviews initiative!
*** see "Beast: Version 100, ACCEPTED to Boost!" here.

Thursday 5 July 2018

QLabel swallows mouse clicks in RichText mode?


As I already said in the title:
QLabel swallows mouse clicks when in rich text mode, what can I do? Help! 
I ran into that problem in my new project just yesterday* and will document the solution here, because searching the Internet didn't result in a thorough solution ๐Ÿ˜ž.
 
Actually, I ran into two problems with Qt (I'm on the 5.9 version now). It started with my desire to change line spacing in the custom icon widget I inherited with a pile of legacy code in my new project. Somehow it wasn't possible with the standard C++ API, so I resorted to the proven Qt workaround - CSS stylesheets.

Oftentimes stylesheet support implements manipulations and parametrizations not available through C++ and this case is one of them. Because setting the stylesheet directly didn't seem to work, the equivalent rich text string would do the work (note that the line-height property is stated in percent values):
  QString txt = "<p style=\"line-height:80;\">text text text</p>";
That trick seemed to have removed the problem, but in reality I just didn't notice the consecutive problem I created with this!

The text I wanted to space was a subscript under an icon, which was set as a bitmap in the class containing both the icon and the text. What I didn't notice was the fact, that a touch/click on the icon would start processing but when the user clicked/touched the re-spaced text it wouldn't! And of course falling back to a non-rich text would fix that problem!

As it turned out, in rich text mode QLabel wouldn't receive any MouseRelease events! First of all I found a slew of Qt bug-reports: 12982, 2028, 24375, all of them rejected or simply not fixed. So is it a Qt bug? Why it's not fixed? And what is a workaround for it? So I started to search the web for hints. One info I found was a short explanation by Thiago Macieira:
"Probably not a bug. Do you get the mousePressEvent? The release event is always sent to the same widget that accepted the mouse release. And QLabel with rich text might contain links, so it may need to handle mouse presses."
Needled to say I was rather devastated - this seems to be the expected behavior, and if you want to change it, you have to reimplement it!!! Needless to say, I wasn't inclined to do so. I was slowly approaching desperation, but then I found another hint"you could try ... Qt::WA_TransparentForMouseEvents". And that put every piece of the puzzle in its place for me:

1. As Qt-documentation says on Qt::WA_TransparentForMouseEvents:
"When enabled, this attribute disables the delivery of mouse events to the widget and its children. Mouse events are delivered to other widgets as if the widget and its children were not present in the widget hierarchy; mouse clicks and other events effectively "pass through" them. This attribute is disabled by default."
2. QLabel might contain links in general, but in out case we just have some plain text which has to be styled, ergo mouse events are of no use to it!

3. We cannot force QLabel to not accept() the mouse events in that case (alas), but we can cut it off from them, so that it won't steal MouseRelease and its parent widget will still receive it.

So I wrote a following method for setting the labels of icons:
  void CIconCell::SetIconLabel(const QString& label)
  {
    // correct spacing in double row texts
    // - cannot be done with std. Qt-API!
    m_Text->setTextFormat(Qt::RichText);
    m_Text->setAttribute(Qt::WA_TransparentForMouseEvents, true); // otherwise rich-text QLabel blocks mouse events!

    auto richTxt = QString("<p style=\"line-height:%1;\">").arg(m_LineSpacingRatio);
    richTxt += label;
    richTxt.replace("\n", "<br />");
    richTxt += "</p>";

    m_Text->setText(richTxt);
  }
PS: somehow the code formatter seems to interpret <br /> as line break, it should be: richTxt.replace("\n", "<br />");

TL;DR;
So essentially all the information was out there, you only had to connect the dots. But this isn't cost free, so I publish the ready solution for the good of all Qt programmers ๐Ÿ˜‰.

---
* OK, not entirely true, but at least I started to write this post on the next day...