Home Closet Why C++ is not suitable for writing graphical user interfaces

Why C++ is not suitable for writing graphical user interfaces

by admin

I love C++, but…

I should point out right away that C++ is my favorite language, I’ve been writing in it practically "since childhood" and to deny its importance as best one of the best languages for writing programs for any purpose, I won’t. Moreover, I don’t see the point in starting another "chorus" or "pointer" contest. This article is just a description of an unfortunate experience with the language, explaining some aspects of it which knowledge will help other programmers in the future.
I once came across an evolving GUI class library. In terms of C++, and more specifically its classes, instances and hierarchies, this language seems incredibly close to the concept of GUI controls, in particular elements such as widgets, class windows and subwindows. The OO models of C++ and the window system are nevertheless different. C++ was designed as a "static" language with lexeme coverage, static type checking and hierarchies defined at compile time. Windows and their objects, on the other hand, are dynamic by nature, they usually live outside the scope of a single procedure or unit with which they are created; widget hierarchies are largely determined by location, visibility and event threads. The fundamentals of the graphical user interface, such as dynamic and geometric hierarchies of windows and controls and event flows, are not directly supported by the C++ syntax or its semantics. Thus, these functions have to be reproduced in the C++ GUI code. This leads to the duplication of graphical toolkit or window manager functionality, the code gets "bloated", we are forced to abandon many "strong" features of C++ (for example, type checking at compile time). Here are some simple examples of C++ / GUI "mismatches".

Do not create constructors (or at least do not use them)

When a derived class overrides a virtual method of the parent class, you should keep in mind that the override does not take effect while the base class constructor is running. This is especially annoying when objects request widgets that respond to GUI events. Suppose that the class Basic_Window. was designed to create a vanilla black and white window on the screen :
class Basic_Window.{
public:
Basic_Window(Rect rect) { gt_create_window(rect, visible, this); }
virtual void handle_create_event(){ set_background(WHITE); }
};

Here gt_create_window() is responsible for the low-level call of the basic graphical toolkit (e.g, xvt_win_create() ). This function allocates space for toolkit data, notifies the window manager, registers this object as an event receiver, and in the example above, initializes the graphical output to the window on the screen.
Suppose we want to create an instance of Basic_Window but with a red background. Normally, to change the behavior of a class, we have to extract and override the corresponding virtual methods. We write :
class RedWindow:public Basic_Window {
virtual void handle_create_event() { set_background(RED); }
public:
RedWindow(Rect rect) : Basic_Window(Rect rect) {}
};
RedWindowred_window(default_rect);

But red_window will appear white, not red! To create a RedWindow , the parent object must be created first. After completing the Basic_Window::Basic_Window() , the virtual tables RedWindow take effect, method handle_create_event() becomes inapplicable, and the constructor RedWindow() is executed. The constructor Basic_Window() registers a graphical toolbox object that instantly starts sending events to the object (e.g., a CREATE event). Constructor Basic_Window() is not yet finished (it is not guaranteed), so the overridden virtual method is not yet in place. Thus, the CREATE event will be handled Basic_Window::handle_create_event() Virtual Tables RedWindow class will be created only when the base class is completely built, that is, when the window is already on the screen. Changing the color of the window at this point will result in an unfortunate error.
There is a simple workaround : prohibit each constructor from registering a graphical toolkit object. Event handling will be structured in such a way as to hold back the end of initialization to derived classes. It is very tempting to think of the widgets on the screen as the "face" of the GUI application object in memory. As the above example shows, this link between the screen and the C++ object is not easy to implement: they are born separately.

No syntactic means of event switching

Suppose the class library includes the graphical interface of the class PictWindow which displays a picture in a window :
class PictWindow{
Picture picture;
public:
virtual void repaint() { gt_draw_pict(picture); }

};
We would like to overlay a small rectangle in a certain area of the image. To this end, we can try to create a subclass of PictWindow :
class OvWindow: public PictWindow {
Rect rect;
virtual void repaint() { gt_draw_rect(rect); }
};

Unfortunately, when we create an instance of OvWindow we see only a rectangle in an empty window, and no image. From the moment the OvWindow::repaint() overrides PictWindow::repaint() , the latter function will not be called when the window should be rendered. We should have implemented OvWindow So :
class OvWindow : public PictWindow {
Rect rect;
virtual void repaint() { PictWindow::repaint(); gt_draw_rect(rect); }
public:
OvWindow(void) : PictWindow() {}
};

Constructor OvWindow is outlined to emphasize that the method OvWindow::repaint() should be deferred to the superclass, as constructors do. Indeed, the constructor of a derived object calls the constructor of the corresponding object from the beginning. repaint() should be deferred to its parent : method in the base class that overrides it.

Bottom Line : Poor C++/GUI compatibility

C++ was designed as a "static" language :

  • with lexeme tracking
  • by checking of static types
  • with static class hierarchies
  • without "garbage collection"
  • with message system without defined compile time hierarchies

GUI objects :

  • are characterized by dynamic objects, and often one-of-a-kind
  • tend to live far beyond the framework in which they were created
  • Hierarchies are determined largely by event streams and their locations, not by class inheritance
  • Hierarchies are built and destroyed at runtime, often in response to unpredictable user actions

C++ is not designed to support dynamic protection of messaging and transmissions (except for exceptions). All this leads to duplication of graphical tools and window manager functionality, code bloat, use of insecure functions, and abandonment of many C++ strengths.

Conclusions

Of course, all these "snags" are not fatal. C++ is a universal and powerful language and is therefore capable of expressing all possible algorithms of computation. Therefore, if an application requires dynamic functions, such as those in Tcl / Tk. , Scheme / Tk , Postscript and the like; using C++, you can always do something along their lines. On the other hand, why not use a language where all these features are present?

You may also like