⬆️ ⬇️

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

I love C ++, but ...





I’ll just make a reservation that C ++ is my favorite language, I’m writing it practically “from childhood” and I will not deny its importance as the best one of the best languages ​​for writing programs for any purpose. Moreover, I see no reason to start a regular holivar or to be measured by “pointers”. This article is just a description of a bad experience with a language, explaining some of its aspects, the knowledge of which will help other programmers in the future.



Once I ran into a developing class GUI library. From the point of view of C ++, or rather of its classes, instances, and hierarchies, this language seems incredibly close to the GUI management concept, in particular, such elements as widgets, class windows and a sub-window. OO C ++ models and window systems are nevertheless different. C ++ was conceived as a “static” language with lexeme scope, static type checking and hierarchies defined at compile time. Windows and their objects, on the other hand, are inherently dynamic, they usually live outside the framework of a separate procedure or block through which they were created; widget hierarchies are largely determined by location, visibility and event streams. The fundamentals of a graphical user interface, such as dynamic and geometric hierarchies of windows and controls, the flow of events, are not directly supported by C ++ syntax or its semantics. Thus, these functions must be reproduced in C ++ GUI code. This leads to duplication of the graphical toolkit, or the functionality of the window manager, the code “swells up”, we have to abandon many of the “strong” features of C ++ (for example, type checking during compilation). The article provides a few simple examples of C ++ / GUI "not docking".



')

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





When the resulting class overrides the virtual method of the parent class, it must be borne in mind that the override does not take effect as long as the base class constructor is executed. This is especially annoying when objects request widgets that respond to GUI events. Suppose that the Basic_Window class 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 invocation of the main graphical toolkit (for example, xvt_win_create () ). This function allocates space for the toolkit data, notifies the window manager, registers this object as the 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. Usually, to change the behavior of a class, you need to extract from it and override the corresponding virtual methods. We are writing:



class RedWindow : public Basic_Window {

virtual void handle_create_event() { set_background(RED); }

public:

RedWindow(Rect rect) : Basic_Window(Rect rect) {}

};

RedWindow red_window(default_rect);




But red_window will appear white, not red! To create a RedWindow , the parent must be created first. After completing Basic_Window :: Basic_Window () , the RedWindow virtual tables take effect, the handle_create_event () method becomes inapplicable, and the RedWindow () constructor is executed. The Basic_Window () constructor registers a graphical tool object that instantly starts sending events to the object (for example, a CREATE event). The Basic_Window () constructor is not yet complete (this is not guaranteed), so the overridden virtual method is not yet in place. Thus, the CREATE event will be handled by Basic_Window :: handle_create_event () . Virtual tables of the RedWindow class will be created only when the base class is fully built, that is, when the window is already on the screen. Changing the color of the window at this stage will lead to an annoying error.



There is a simple workaround: prevent each constructor from registering a graphical tool object. Event handling will be constructed in such a way as to keep the end of initialization to derived classes. It is very tempting to view widgets on the screen as the “face” of an application's GUI object in memory. As the example above shows, this relationship between the screen and the C ++ object is not so simple to implement: they are born separately.



No syntax switching event





Suppose that the class library includes a PictWindow class GUI that displays a photo in a window:



class PictWindow {

Picture picture;

public:

virtual void repaint() { gt_draw_pict(picture); }

...

};




We would like to impose a small rectangle in a certain area of ​​the image. To this end, we can try to subclass PictWindow :



class OvWindow : public PictWindow {

Rect rect;

virtual void repaint() { gt_draw_rect(rect); }

};




Unfortunately, when we create an instance of OvWindow , we will only see a rectangle in an empty window, and no image. From the moment OvWindow :: repaint () overrides PictWindow :: repaint () , the last function will not be called when the window is to be drawn. We had to implement an OvWindow like this:



class OvWindow : public PictWindow {

Rect rect;

virtual void repaint() { PictWindow::repaint(); gt_draw_rect(rect); }

public:

OvWindow(void) : PictWindow() {}

};





The OvWindow constructor is stated to emphasize that the OvWindow :: repaint () method must be deferred to the superclass, as the constructors do. In fact, the constructor of the derived object from the beginning calls the constructor of the corresponding object. repaint () should be postponed to its parent: method in the base class that overrides it.



In summary: Poor C ++ / GUI compatibility





C ++ was developed as a “static” language:





GUI objects:





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



findings





Of course, all these "snags" are not fatal. C ++ is a universal and powerful language and, therefore, is able to express all possible calculation algorithms. Therefore, if an application requires dynamic functions, such as those found in Tcl / Tk , Scheme / Tk , Postscript, and the like; using C ++, you can always do something by their example. On the other hand, why not use a language where all these traits are present?

Source: https://habr.com/ru/post/93611/



All Articles