Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

A User Interface System

4.60/5 (7 votes)
16 Sep 2009CPOL5 min read 32.2K   354  
A user interface infrastructure quickly attachable to your application, accompanied by a simple text-based platform-independent user interface that builds on the infrastucture.

Introduction

Almost every program needs some way of communicating with the user. Those programs that don't (drivers, system processes, etc.) still benefit from being able to communicate with the person developing it, making debugging a bit easier.

Many approaches can be taken to get the information exchanged, ranging from initializing configuration files and command-line switches, to runtime signals and actual prompting for input using the shell. Implementing those features usually needs specific attention by the programmer, causing him tedious, lengthy work.

But this work can be reduced immensely. After all, the only things that are done by a user are changes to variables and invocations of functions. Thus, an optimal solution would be to just tell the machine which variables and functions to make accessible (to publish) to the user. The communication with the user can then be exported to other standard once-implemented code, sparing this work from the programmer. In other words, the programmer simply has to stick to the (very few) functions defined by an infrastructure and not worry about the implementation of a user interface.

That approach also allows for different implementations of the user interface, offering great flexibility. Those implementations could not just vary between text-based and graphical, but among user prompting, configuration files, command-line switches, network protocols, etc.

A whole list of (dis-)advantages can be found at the end of the article.

Note: Although the concept presented here is universal, the code is only applicable to object oriented rather than modular programming. The file UI.hpp describes the changes that need to be done to make the infrastructure fit for modular programming.

The Infrastructure

The whole basis for the infrastructure presented in this code is one single abstract class named ui_export.

C++
class ui_export
{   ...
    virtual void addoptions()=0; 
    void postoption(string name, string desc, xxx);
    ...
}

Every class that wants to publish some member variables to the user needs to inherit from this class. By doing that, it needs to implement the function addoptions. The implementation of addoptions ought to consist only of calls to the postoption function, which is also inherited from ui_export.

C++
class yourClass : public ui_export
{   void addoptions()
    { postoption(...);
      postoption(...);
      ...
    }
}

The postoption function needs to be called in addoptions for every single option that is to be published. It has 14 different implementations allowing for different data types and functions to be published. Those are:

  • classes derived from ui_export
  • C++
    void postoption(string name, string desc, class ui_export* post)
  • simple functions of type void f()
  • C++
    template<class X, class Y>
    void postoption(string name, string desc, X* Object, void (Y::* F)() )
  • variables of type bool, int, double, char, char*, string
  • C++
    void postoption(string name, string desc, xxx* post)
  • get and set functions for types: bool, int, double, char, char*, string
  • C++
    template<class X, class Y>
    void postoption(string name, string desc, X* Object, xxx (Y::* getF)(), 
                                                        void (Y::* setF)(xxx) )

The following example illustrates what the calls to the above functions would look like:

C++
class car : public ui_export
{   ...
    class engine eng;
    void start() {};
    int weight;
    bool _lights;
    bool getlights() {return(_lights);}
    void setlights(bool par) {_lights=par; repaint();}
    ...
    void addoptions()
    {   postoption("eng", "manipulate the engine object", &eng);
        postoption("start", "start the car", this, &car::start);
        postoption("weight", "weight of the car in kg", &weight);
        postoption("lights", "headlights of the car", this, &car::getlights, 
                                                            &car::setlights);
    }
};

After that is done, the class is usable by a user interface.

The User Interface

The user interface supplied with this code is text-based. It only uses the C++ Standard Library, which makes it platform independent. It has a menu-driven feel to it caused by the class oriented approach of the infrastructure.

Image 1

Again, the whole basis for the user interface is a single class named user_interface. To use it, an instance of that class needs to be made. Being a subclass of ui_export, user_interface implements all the postoption functions. Those are used to initialize the user interface. The operator() function starts the user interface.

C++
user_interface ui;
ui.postoption("o", "manipulate obj", &obj);
ui.postoption(...);
...
ui();

That's it.

Example

The following is a shortened version of 'Demo1YourApp.cpp'. It demonstrates what a simple program using the user interface looks like:

C++
#include"UI.hpp"

class yourClass : public ui_export 
{   public:
        bool b; int i;
        void addoptions()
        {
            postoption("b", "mess with a boolean", &b);
            postoption("i", "mess with an integer", &i);
        }
};

int main(int argc, char* argv)
{
    yourClass c;
    user_interface ui;
    ui.postoption("yourClass", "mess around with yourClass", &c);
    ui();
}

Saving Function

The user interface has a simple "save state" mechanism. It can be used to initialize your application, but isn't meant to actually save the state of a complex application (startup would take a lot of time). When the user interface is left, it will ask you to either (Q)uit (without saving) or to (S)ave (and quit).

The way it works is to simply remember all user input and save it in a file. On startup, the user interface opens the file (default filename is 'input') and executes all the input previously saved. The file names for the load and save file can be accessed through the user interface object:

C++
ui.loadfilename="init";
ui.savefilename="save";

The name of the save file can also be changed during user interface execution. This is done by using the SaveFile option that is posted by default.

You can also edit the savefile containing the saved input by hand.

The C++ Standard Library

Another really great advantage that comes with the concept of the infrastructure is that common libraries like the C++ Standard Library can be made UI-capable in advance. This again reduces the work for the programmer and ensures a uniform behaviour of different programs which is nice for the user.

A start has been made. The file stdUI.hpp is steered towards building UI-capable classes along the C++ Standard Library classes in the namespace stdUI. The following classes are already available:

  • stdUI::vector
  • stdUI::ios_base
  • stdUI::basic_ios
  • stdUI::basic_istream
  • stdUI::basic_ostream
  • stdUI::basic_iostream
  • stdUI::basic_stringstream
  • stdUI::basic_fstream

The non-basic versions are also available.

Example

A small demonstration of the UI-capability of the classes stdUI::iostream, stdUI::stringstream and stdUI::fstream as well as the objects stdUI::cout and stdUI::cin is found in the file 'Demo2StreamLib.cpp'.

Conclusion

Advantages

  • No more mechanism for user interaction to be entirely implemented
  • Programmer only needs to know (very few) infrastructure commands
  • Allows for multiple implementations for the user interface (text-based, graphical, config file parsing, command-line switches)
  • Simple initializing or state saving capabilities
  • Universal user experience for different programs
  • (Almost too) great amount of editability of program parameters (good for debugging, bad for user)
  • Common libraries like the C++ Standard Library can be published in advance further reducing the workload

Disadvantages

  • Code is very class oriented (see comment on modular programming)
  • No information hiding / once published options cannot be hidden
  • (Causes a big project to become somewhat "option bloated")

Further Reading

A lot of the more technical documentation can be found in the source. Topics are:

  • Modular programming alterations
  • Why delegates
  • Building a modified version of the C++ Standard Library classes through the use of MVI

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)