Lit Window Productions

Documentation
Download
simpler UI Coding
Articles
How it works
Rapid UI development
Data Adapters 1
Donating

RSS Feed
Whats this?

Contact
Privacy

Fast, easy dialog editing and code generation with Anthemion’s Dialog Editor for wxWidgets...

[Articles] [How it works] [Rapid UI development] [Data Adapters 1]
More articles:

The Lit Window Library

All these ideas and requirements are finally starting to come together in “The Lit Window Library”. It is a software library, not a code generator, design tool or component. It contains three elements:

  • data abstraction layer
  • constraint solver and rule programming language
  • library of reusable algorithms

Data abstraction layer – data adapters

All operating system have some kind of “hardware abstraction layer”. Application programmers can access the display, network and other peripherals without knowing what hardware will actually be installed in the users system. The Lit Window Library uses an abstraction layer for accessing data like operating systems use one for accessing hardware. Library programmer can access data structures without knowing what the actual definition at the “user” will be. The “users” are the people using the library. This data abstraction layer is called “data adapters”.

Data adapters are akin to extended runtime information or reflections, but go beyond that. They provide an abstraction layer to fundamental data types (int, float, bool etc...), aggregates (struct and class) and containers. This abstraction makes it possible to write truly generic algorithms, compile them and store them away – in binary form if desired – regardless of the data structure the algorithm will later work with. This is different from C++ templates where data types have to be known at compile time.

Take “SaveSettings” for example. The algorithm is really extremely simple.

  1. Iterate over all elements you want to save.
  2. Save each element to storage using a save method according to its type: int, float, bool, string, aggregate or container.

In C++ you cannot iterate over elements of a struct. The Lit Window Library adds this capability. In pseudo language:

void WriteAggregate(elements_t &elements)
{
  elements_t::iterator i;
  for (i=elements.begin(); i!=elements().end(); ++i) {
    switch (i->type()) {
      case Int_t: WriteInt(i->as_int()); break;
      case Float_t: WriteFloat(i->as_float()); break;
[...]
      case String_t: WriteString(i->as_string()); break;
      case Aggregate_t: WriteAggregate((*i)); break;
      case Container_t: WriteContainer((*i)); break;
    }
  }
}

Compile this method, put it in a library and a user of the library can save any of his data types by calling

MyOwnDataType t;
WriteAggregate(t);

You will never again have to enumerate the members of MyOwnDataType in your source code, by hand:

WriteInt(t.m_someInt);
WriteInt(t.m_someOtherInt);
WriteBool(t.m_andABool);
WriteInt(t.m_ohNo_AnotherInt);
WriteInt(t.m_doesntThisEverEnd);
WriteSpecial(t.m_specialSigh);

Another example is showing a list of elements in a grid control. This is very common in front ends for databases, but also occurs in programs with no database in sight. What you usually do is either use a specialized grid component that accepts a database connection or do it by hand:

for (i=0; i<maxElements; ++i) {
   element=GetElement(i);    // or ODBC.GetNext()
   grid(0, i).SetIcon(get_icon_for(element.m_someInt));
   grid(1, i).SetInt(element.m_someOtherInt);
   grid(2, i).SetBool(element.m_andABool);
...
}

And so on. And again next time you have to fill another grid or list control with a different set of elements.

Just like the WriteSettings method, The Lit Window Library lets you write this FillGrid method once, put it in a library and then reuse it with only a minimum of effort.

void FillGrid(container_t &aContainer, grid_t &grid)
{
   container_t::const_iterator i;
   size_t row=0;
   for (i=aContainer.begin(); i!=aContainer.end(); ++i, ++row) {
      elements_t::const_iterator e;
      // iterate over all members of the current element
      size_t column=0;
      for (e=i->begin(); i!=i->end(); ++e, ++column) {
         grid(column, row).Set(*e);
      }
   }
}

Using this is simple.

std::vector<MyOwnDataType> myVector;
FillGrid(myVector, myGrid);

The container abstraction layer makes the FillGrid method even independent of the container implementation. If you want to use an ODBC data table instead of an STL::vector, here is the code:

wxODBC myTable(“SOMETABLE”);
FillGrid(myTable, myGrid);

To the Lit Window Library, both containers look the same. They both have the same interface: a const_iterator, an iterator, a begin, end method and a couple of other methods. The abstraction layer routes the libraries function calls to the correct implementation.

UI by the rules – a simple constraints solver for UI requirements

When I took a class in constraints programming, I was fascinated by the way constraints programming solved the 8 -queen problem (put 8 queens on a checker board so that no queen threatens any other). I was so used to sequential thinking that I was really surprised to see how easy programming something like that could be. Traditionally I’d have written a classic backtracking algorithm: place a queen, see if it threatens any other queen. If it does not, place the next queen and descend recursively. If it does, undo placement and try the next possible position.

The constraints based solution was much simpler. I would simply write down the constraints: For each queen ==> number of queens threatened by it == 0. Total number of queens == 8. (This was a long time ago, I don’t remember the exact source code.) Then: “Go, solve!” The constraints solving algorithm would do the rest and come up with a valid solution seemingly “all of its own”. It was certainly much easier to let it figure out the exact way how to solve the problem.

The same idea works great for UI requirements. When you think about it, UI requirements are really nothing but a set of rules.

  • The user can enter Value1 only when in Administrator mode.
  • When CheckboxXYZ is unchecked, Elements A,B,C must be disabled.
  • The form MyDetails shall always display the details of the database row the user has currently selected in list controlTheList.

Traditionally to implement the above requirements, we’d code a lot of OnSomething methods, intercepting events, updating other controls. Wouldn’t it be a lot nicer if we could somehow tell the computer to follow the rules instead? Instead of coding

void OnTheListChanged()
{
   do a lot of work here to update the form MyDetail
}

simply write

MyDetails.Contents = TheListBox.Current

All user interface APIs I know of use the “properties, methods, events” paradigm. This is a very reliable way to construct a user interface, but it offers no support for “connecting” widgets, making one or a group of widgets react to another widget. You are sent an event and then are on your own. Coding the interactions between widgets is what I spent most of my time for.

The Lit Window Library adds “Rules” to the paradigm. And the required constraints solver is even simpler than the one necessary for the 8-queen problem.

To be continued…

[Home] [Products] [Shop] [Knowhow] [Library] [About]

© 2004, Hajo Kirchhoff, last modified Mar 06, 2008

back to top