Informazio orokorra
Adibideak
Pantailak
Konparazioak
Eskaerak
Download
Documentation
Bazaar
Estatuko eta Laneko Plan
Maiz egiten diren galderak
Idazlea eta Lizentzia
Forums
U++ finantzaketa
Bilatu web honetan
Language
euskara













SourceForge.net Logo



Web gune hau ez da itzuli. Translate da?

 

Overview Ultimate++

 

Whetting your appetite

Ultimate++ promises radical reduction of code complexity of typical desktop applications. Let us start with a simple example - an application that displays the number of days between two dates. The number of days is refreshed as user types or edits dates into the input fields:

 

 

The application window layout is created using Ultimate++ visual designer:

 

 

The actual code for the application is as complex as this:

 

#include <CtrlLib/CtrlLib.h>

 

#define LAYOUTFILE <Days/Days.lay>

#include <CtrlCore/lay.h>

 

class Days : public WithDaysLayout<TopWindow> {

public:

    void Compute();

 

    typedef Days CLASSNAME;

    Days();

};

 

void Days::Compute()

{

    result = IsNull(date1) || IsNull(date2) ? "" :

             Format("There is %d day(s) between %` and %`",

                   abs(Date(~date1) - Date(~date2)), ~date1, ~date2);

}

 

Days::Days()

{

    CtrlLayout(*this, "Days");

    date1 <<= THISBACK(Compute);

    date2 <<= THISBACK(Compute);

}

 

GUI_APP_MAIN

{

    Days().Run();

}

 

Everything belongs somewhere

In Ultimate++, most objects are bound to some logical scope. As a result, you will not see many new operators in code using Ultimate++ and almost no delete operators outside the implementation of containers.

That of course does not mean you are not allowed to use pointers, but it is good practice to use pointers just to point to things, never to manage heap resources. This also avoids all confusion regarding ownership of the underlying object, time of its deletion etc. If you need to manage data sets of variable size or polymorphic type, you should prefer using one of Ultimate++ containers.

Speaking about it, there are no shared smart pointers (like boost::shared_ptr) in Ultimate++ used to manage heap resources at interface level. They are not needed and considered bad practice.

In C++, this approach proves to be equally good or better than garbage collected languages like Java or C#. While those languages are able to provide automatic management of heap resources, U++ approach provides very deterministic automatic management of all resources.

Ultimate++ containers

One aspect of Ultimate++ is bringing a lot of criticism: Ultimate++ is not using much of standard C++ library. There are, however, serious reasons for this. STL, with its devastating requirement that each element stored in container has to have copy-constructor, makes standard containers somewhat hard to use in GUI development.

There is no such general requirement for Ultimate++ containers. Instead, Ultimate++ containers come in two flavors.

Vector flavor has Moveable requirement that allows very fast implementation for certain types (e.g., element insertion at arbitrary position of Ultimate++ Vector<String> is more than 10 times faster than the same operation with typical implementation of std::vector<std::string>).

Array flavor has no requirements for element types at all, at the price of somewhat lower performance.

As a result, in Ultimate++ you are for example allowed to create container of .GUI widgets that edits integer numbers ( Array<EditInt> integer_editors) and even sort it using standard Ultimate++ Sort algorithm. Doing something like this would require using pointers as elements in STL (std::vector<EditInt *>) or alternatively some sort of smart pointers (soon to be std:: boost::shared_ptr), but both increase code complexity and break the Ultimate++ rule according to which everything belongs somewhere.

Who owns widgets

One of the things we discovered over our countless experiments with C++ GUI is the fact that the GUI toolkit should not own widget objects. GUI objects should be always owned by the client, belonging to some scope of client code (everything belongs somewhere). GUI toolkit just references them, it neither creates them nor destroys them. Each widget object can play its GUI role in some context (like being visible in some window), but at the same time it is always a stand-alone entity with its set of attributes that can be modified or queried regardless of its current GUI status.

This has many serious implications. The most important is that Ultimate++ does not require widget objects to be allocated on the heap. That in turn allows us to arrange GUI dialog structure in a very effective way, instead of

 

struct MyDialog {

    Option *option;

    EditField *edit;

    Button *ok;

};

 

we are using:

 

struct MyDialog {

    Option option;

    EditField edit;

    Button ok;

};

 

Even more important, lifetime of these widgets does not depend on the life cycle of MyDialog GUI - MyDialog can be closed or not yet open, but attributes of widgets are accessible all the time.

Dialog templates are C++ templates

Now that we have laid down the foundations, it is time to introduce the coolest aspect of Ultimate++ GUI programming - layout templates:

If you visually design a layout (usually, but not limited to, the layout of a dialog box) using TheIDE's Layout designer, this layout is in in your code reflected as a C++ template that derives from a widget-based class and declares all widgets as its member variables, and a matching function (InitLayout) that sets up the widget positions and their pre-designed attribute defaults.

For example, such a template would look like this:

 

template <class T>

struct WithMyDialogLayout : public T {

    Option option;

    EditField edit;

    Button ok;

};

 

template <class T>

void InitLayout(WithMyDialogLayout<T> *layout, ...);

// implementation details omitted

 

The reason why it is provided as a template rather than a simple class or struct is that in this way you can use any widget type as its base class, not just the one that represents dialog windows (TopWindow).

This approach provides radical reduction of complexity - many annoying things that seem to be necessary to identify widgets in client code (like widget IDs or names) are simply gone for good. All you have to deal with in Ultimate++ are your instance variables.

Value and Null

One aspect that makes development in Ultimate++ very orthogonal is the existence of Value - the polymorphic value type. Any of Ultimate++ basic types (int, double, String, Color, Rect, Font, Image etc...) can be stored into and retrieved from a Value. Value itself can be queried for the type of value it contains. It is also very easy to make any custom types Value-compatible.

Related to Value is the general concept of "empty value". The Ultimate++ special constant Null represents an empty value. Most concrete types support Null. Null is also defined for fundamental types - int, double and int64 - as a value that is lower than any other value for specific type (for example, Null is equal to INT_MIN for int). To test whether a variable of a certain type is Null, you can use the generic IsNull function.

Value (and Null) have a remarkable effect on GUI flexibility. Many widgets logically have their "natural" values, (for integer edit field it is the typed in number, for option widget it is either true or false according to its state) and Ultimate++ provides uniform access to these values via Value and GetData / SetData virtual methods. For example, clearing a dialog can be usually done by assigning Null to all of its widgets.

Display and Convert

Display and Convert based classes further enhance Ultimate++ flexibility using Value.

Convert classes act as bidirectional Value to Value converters. Usually, but not limited to, this conversion is between the value of a logical type and its textual representation (conversion of the textual representation to the logical type can be sometimes omitted). Examples are ConvertInt or ConvertDate.

Many Ultimate++ widgets are able to use these Convert classes as properties. An example is the EditField class, a generic input field. By assigning specific Convert based class to EditField, you can "teach" it to edit numbers, dates or anything that has textual representation.

Somewhat similar to Convert classes are Display based classes. These are classes that describe how Values should be displayed. Once again, many Ultimate++ widgets are using Display classes as their properties. For example, to "teach" the DropList widget (DropList is close to something called "combo box" on other platforms) to display colors, all you need to do is to set its Display attribute to DisplayColor (remember, Color is Value compatible and DropList's list consists of Values). Meanwhile, you can use the same DisplayColor as the property of many other widget classes.

Callbacks

While virtual methods provide a great way to organize the input interface of GUI widgets (like mouse or keyboard input), each GUI toolkit has to provide effective means for output interfaces as well (if you do not know what output interface is: when a button widget is pressed, the output interface is responsible for delivering this information to the client code).

Our solution to these needs is called a Callback. You can think about Callbacks as a very generalized form of function pointers. Each Callback represents some kind of action - usually this comprises calling a certain function or a certain object method - that can be invoked at any time.

Callbacks are generic and can take some very interesting forms. For example, a type of Callback does the simple task of calling two other given Callbacks, providing a very simple tool for grouping. There are Callbacks that take no argument, but call a function or method with an argument when invoked - this additional argument is stored within Callback during its construction. To illustrate this important feature, see the following code snippet:

 

void MyDlg::SetEditorValue(int x)

{

    editor <<= x;

}

 

MyDlg::MyDlg()

{

    button1 <<= THISBACK1(SetEditorValue, 1);

    button2 <<= THISBACK1(SetEditorValue, 2);

 

In this snippet, we have two buttons and one integer input field. Pressing the first or second button sets the input field to the value 1 or 2 respectively.

It is also very important that Callbacks are completely decoupled from classes. While they can invoke specific methods of certain object instances, there are no further requirements for the method (beyond signature) or the class of the object.

Just to make things clear for those familiar with boost libraries - yes, Callback classes are in fact very similar to boost::function, with interface polished a little bit more toward the needs of Ultimate++ framework (they are Moveable - can be stored in Vector flavor of containers).

Ultimate++ set of widgets

While the standard set of U++ widgets is less important to us than the general principles, partly due to the fact that creating new widget classes is often a trivial task in U++, any description of toolkit would be incomplete without it.

So here follows an incomplete but representative list:

Label, Button and Option are basic well known widgets.

Switch is something usually called "a group of radio-buttons", anyway in the U++ this is a single widget (this way, reading the Value of a switch is much more consistent).

EditField, EditInt, EditDouble, EditIntSpin, EditDate, EditString are basic input fields. Note that U++ provides distinct types of input fields for specific value types.

LineEdit and DocEdit are two kinds of plain text editors. LineEdit works with lines while DocEdit works with paragraphs.

ScrollBar and ScrollBars. While their names are self-explaining (ScrollBars is just pair a consisting of a vertical and horizontal ScrollBar), it is worth noting that the U++ ScrollBar also provides all calculations for position of view area.

Slider is an "analog" input widget whose value is determined by position of "thumb".

HeaderCtrl represents headers of various tables, namely ArrayCtrl

ArrayCtrl is perhaps the most complex and complicated widget in Ultimate++. It is basically a table widget used to operate on Value matrices. It can combine Values to be displayed (using Display class) as columns (yes, several Values in row can be combined into single a column using Convert if needed) and edit them using slave Ctrls (those can be inside the table displayed on user "edit" action, inside the table always visible or outside the table in the dialog box displaying Values of currently selected line).

Option, EditString, DropList, Switch and ArrayCtrl in action.

SqlArray is derived from ArrayCtrl and adds abilities to act as SQL table editor, including master-detail capabilities.

Splitter is used to implement split view widgets with an adjustable bar.

ProgressIndicator can be used to indicate progress of lengthy operations.

TabCtrl is used for dialogs with tabs.

TreeCtrl is used to display arbitrary tree hierarchies.

ColorSelector, ColorPusher and ColorButton are widgets for graphical user color selection.

ColorButton

MenuBar and ToolBar handling is a little unorthodox in Ultimate++, as the menu actions, represented as Callbacks, are passed to the methods constructing the corresponding Bar. This has some serious advantages - state and presence of individual buttons or menu bar items can be easily adjusted according to the current application state. It is also often possible to have a single method for construction of both ToolBar and MenuBar.

ColumnList displays values in user-adjustable number of columns.

FileList is variant of ColumnList for displaying lists of files.

Finally, Ultimate++ has tools to deal with advanced text formatting:

RichText is a class that provides storage of complex text documents, including font and paragraph formatting and even nested tables support.

RichTextView is a widget for viewing RichText texts.

RichEdit is a full-featured RichText word-processor (including spell-checker) in standard widget package, readily available to any U++ application.

RichEdit

 

You can find complete alphabetical list of basic U++ widgets here.

SQL programming

One of the motivations behind Ultimate++ always used to be the development of enterprise class client-server SQL applications. Using general Ultimate++ philosophy we believe to have achieved some extraordinary results, basically making Ultimate++/SQL development easier that using SQL dedicated development tools.

Of course, SQL is an area where the Value abstraction hugely pays off. Fetching database values and putting them to GUI widgets never was as trivial as it is in Ultimate++.

An important tool related to SQL is idea of "SQL expressions". SQL expression is entity that represents SQL command. Ultimate++ provides means to build SQL expression using C++ overloading mechanism. For example, Ultimate++ SQL expression might look like:

 

Select(NAME, SURNAME).From(PERSON).Where(PERSONID == personid);

 

where NAME, SURNAME, PERSON and PERSON are special values of SqlId type, while personid is an ordinary C++ variable. The important thing here is that SQL expressions can be built from smaller subexpressions - that is particulary important when building Where conditions.

 

SqlBool where;

if(!IsNull(findname))

    where = NAME == findname;

if(!IsNull(findsurname))

    where = where && SURNAME == findsurname;

SqlExp exp = Select(PERSONID).From(PERSON).Where(where);

 

When SQL expression is ready for execution, it can be executed on an Sql cursor object using the * operator. After this, you can Fetch the results.

 

Sql sql;

sql * exp;

while(sql.Fetch()) {

    Sql sqlu;

    sqlu * Update(PERSON)(SALARY, SALARY + 100).Where(PERSONID == sql[0])

}

 

Another effective tool is the concept of database schema description files. These are files used to describe a database model using specialized C-macro constructions:

 

TABLE_(PERSON)

    INT_     (PERSONID) PRIMARY_KEY

    STRING_  (NAME, 200)

    STRING_  (SURNAME, 200)

    DOUBLE_  (SALARY)

END_TABLE

 

These description files are then used to synchronize the database model on the SQL server, to generate SqlId constants used in SQL expressions, and, last but not least, to generate C++ structures (named after the tables with S_ prefix) that can be used to form SQL expressions and to fetch query results:

 

S_PERSON person;

SQL * Select(person).From(PERSON);

while(SQL.Fetch(person))

    person_table.Add(person.PERSONID, person.NAME, person.SURNAME);

 

Thanks to the Value concept described above, most widgets couple seamlessly with SQL code out of box. One of the tools that exploit these capabilities is the SqlCtrls class that orchestrates the data interchange between dialog widgets and database records:

 

void EditPerson(int persionid) {

    WithPersonLayout<TopWindow> dlg;

    SqlCtrls ctrls;

    ctrls(PERSON, dlg.person)(NAME, dlg.name)(SURNAME, dlg.surname);

    SQL * Select(ctrls).From(PERSON).Where(PERSONID == personid);

    ctrls.Fetch(SQL);

    if(dlg.Run() == IDOK)

        SQL * ctrls.Update(PERSON).Where(PERSONID == personid);

}

 

Summary

In this overview we have tried to summarize the most exciting features of Ultimate++. There are of course many more important features including certain interesting implementation techniques like zero overhead memory allocator, perfect image rescaling etc.

Since the very beginning we have kept using Ultimate++ ourselves to develop applications for our customers. Even so, in recent years we never hesitated to compromise our entire code-base each time we felt that some major or minor aspect of library interface or implementation needed to be improved. This enabled us to slowly develop the library and perfect it into its current state.

Now, after some 11 years of development, Ultimate++ is a mature platform that brings vast reductions of our development costs. Most interfaces seem to be finished and optimal. There is of course still some work ahead, mostly in documentation an IDE department.

If you find our Ultimate++ way of programming interesting, nothing stays in your way to downloading it. But be careful there: you should be prepared to throw away some old habits and usual ways of thinking about how "things are always done", or they might maime your opportunity to receive a lot in the reward, together with a healthy disrespect to certain honorable, well-established development tools.

 

Web gune hau ere english, čeština, deutsch, français eta русский-ez. Lagundu nahi duzu?