I’m using the cross-platform Qt C++ library to write an app for linux, OS X, and Windows. Qt source can be compiled to a wide range of systems and devices, with two obvious omissions that are not likely to be filled any time soon: iPhone and Android. I know I am going to need client code on these devices, and I know that the client code is going to be extremely similar to the Qt client code. How do you design for this?

Applying the concept of separation of concerns, you design your classes into layers. The base layer consists of high level concept classes that perform all fundamental actions using generic data types. Then, the derived layer is used to get the job done, using the most useful and powerful lower level tools at your disposal. Push everything you can into the base layer, until you hit the limit of non-portable information and data types. It’s a beautiful way to code.

To be more specific, in my case, the base class layer will be using C++, including the STL, which gives me tons of power. I will pull in boost if even more horsepower is needed. The derived class layer for the first round of clients will be Qt-powered. The Qt library has a massive amount of real-world usefulness, supporting everything from http to video playback. I have not gotten to the iPhone and Android clients yet so the whole concept may change, but here’s my current plan. The iPhone code will be Objective C with the C++ base class layer linked in. I will attempt to incorporate the C++ base class layer into the Android code using the NDK.

Here’s a quick example of the layer approach, in this case used to quickly set up profiling using Qt’s QTime class at the lower level:

Note the use of std::wstring in the base class (more portable), and QString in the derived class (more powerful). That’s a little bit expensive, but once std::wstring is converted to QString, it can be used anywhere within the Qt library.

MyappProfiling.h



// --------------------
// Profiling
// --------------------
// Profiling can be done generically by third-party tools (gprof callgrind etc)
// but custom timing routines have always been far superior in meeting my needs.
// Also, this allows the end user to turn on profiling and send a log of results.
// We divide profiling into levels - start at the top and dig down as needed.
// --------------------
typedef enum {
    PL_OFF,
    PL_TOP_LEVEL,
    PL_1 = PL_TOP_LEVEL,
    PL_2,
    PL_3,
    PL_4,
    PL_MOST_DETAILED = PL_4,

    PROFILING_LEVEL_COUNT
} PROFILING_LEVEL;

class MyappProfiling
{
public:

    virtual MyappProfiling::~MyappProfiling() {}

    // Example calls:
    //
    //  int n_my_start = start_profiling(PL_1);
    //  myroutine();
    //  end_profiling(n_my_start,"call to myroutine",PL_1);
    //
    // n_time is milliseconds since some baseline (platform can decide)
    virtual int  start_profiling(PROFILING_LEVEL level = PL_TOP_LEVEL) = 0;
    virtual void   end_profiling(int n_time, std::wstring strMessage, PROFILING_LEVEL level = PL_TOP_LEVEL) = 0;

};

QtProfiling.h



class QtProfiling : public MyappProfiling
{
typedef MyappProfiling inherited;

public:

    virtual QtProfiling::~QtProfiling() {}

    virtual int  start_profiling(PROFILING_LEVEL level = PL_TOP_LEVEL);
    virtual void   end_profiling(int n_time, std::wstring strMessage, PROFILING_LEVEL level = PL_TOP_LEVEL);
};

QtProfiling.cpp


// Profiling.
static QTime s_t_profiling_base = QTime::currentTime();
int  QTLocalModel::start_profiling(PROFILING_LEVEL level)
{
    if ( level >= get_pref(IP_PROFILING_LEVEL) )
    {
        int msecs = s_t_profiling_base.msecsTo(QTime::currentTime());

        // Check for rollover and reset if needed.
        // WARNING: any outer-nested profiling blocks will get screwy.
        // This only happens if you're running the app for days at a time.
        // Cest la vie.
        if (msecs < 0)
        {
            s_t_profiling_base = QTime::currentTime();
            return 0;
        }

        return msecs;
    }
    return 0;
}
void   QTLocalModel::end_profiling(int n_time, QString strMessage, PROFILING_LEVEL level)
{
    if ( level <= get_pref(IP_PROFILING_LEVEL) )
    {
        double d_seconds = (s_t_profiling_base.msecsTo(QTime::currentTime())-n_time)/1000.0;
        int seconds = d_seconds;
        int msecs = (d_seconds - seconds)*1000;
        int minutes = seconds/60;
        QString msg(
            QString("PROFILING: %1:%2.%3 for %4").
            arg(minutes,2).
            arg(seconds,2,10,QChar('0')).
            arg(msecs  ,3,10,QChar('0')).
            arg(strMessage)
        );
        m_p_controller->logToSystem(msg);
    }
}

6 Comments

  1. Save the speeches for Neil Kinnock. When are finishing your Max Headroom program?

  2. I have no idea what you just posted Mike. I don’t speak your “language”. 🙂

  3. haha jane u crack me up! just playing with my tech blog, i promise i wont talk like that at the next dinner party, hee…

  4. One of these days you’re going to write code and invent A.I. for the Cylons… They will then takeover and destroy the Earth.. and it will all be your fault.

Leave a Reply to Keith Kizer on Facebook