C++ Drawing Library

21Jan15

Over the last couple of weeks, I’ve been building a graphics/animation library to use in the class that I’m teaching at USC. It’s now in a stable state and has the features that I think I’ll need this semester.

A lot of the motivation comes from Princeton where I taught previously: the class there, taught in Java, uses the awesome book, assignments, and libraries of Kevin Wayne and Bob Sedgewick. StdDraw.java, their library, Just Works™. You don’t need to create any objects, or initialize any canvas, or create a hook. If you want a circle, just call StdDraw.circle(x, y, r). This makes it reasonable to write code for a beginner class that students will actually read, and reasonable for students to write their own clients. So I’ve tried to emulate that.

Of course, with Java, you have the benefit of native platform support, and their library is a wrapper over the java.awt and javax.swing libraries. In my case, after a lot of false starts, the only C++ library that

  • I could get working on my VM
  • I could get making graphics and animation

was “Qt.” More on that is below. But I’m pretty happy with how it turned out.

Here’s a little picture showing it in action:

If you’re interested, here is the:

Caveats

There were a few things that made life unnecessarily difficult, at least for some shlub with a Java/Python background like me.

  1. I have no idea if someone not using the course VM will be able to get it to run, since they’ll have to install Qt. And adding audio & .mid support is another order of magnitude difficult for the student.
  2. Every function call causes a pretty long chain of events, and as a result, there’s a ridiculous amount of boilerplate in draw.cpp, so every function has to be declared 4 times in slightly different ways:
    • The user calls a library function.
    • The library function, a static function, calls a public member function of a QObject.
    • That member function “emits” a protected “signal”.
    • It’s transmitted (through a cross-thread queue) to the “slot” of another object, the GUI’s “QWidget”.
    • That slot actually does the work.
  3. You need threading in order for the student’s calls to be non-blocking, which is important for animation. But clang++ doesn’t support threads properly. So we have to use g++, which is not our course compiler, and is not as portable to OSX.
  4. We really would like to run some stuff (wait for the window to close) after the student’s main() function, but there’s no exposed API for this that worked. So we use the preprocessor to rename their main() method to _main(). It’s awful because you need to transform both a no-argument main() and a main(int, char**) to the same thing. This really makes me feel dirty…
    // transform main() or main(int, char**) to _main(int, char**)
    #define main(...) vamain(__VA_ARGS__)
    #define vamain(...) vamainhelp(,##__VA_ARGS__, int, char**)
    #define vamainhelp(blank, first, second, ...) _main(first, second)

But, I would have to say that I learned a lot in the process. The library even works with gdb for debugging and stepping. It’s weird that Qt seems to try to do a lot of stuff to C++ that other languages support natively: hooks (signals/slots), reflection and multi-threading in particular.

We’ll see how it goes during the semester. Hopefully many of the undergrads working in my course are wizards enough at Qt, from their experience using it in the data structures course, that more people can configure it on whatever machine they like. My fingers are crossed!

Advertisements


3 Responses to “C++ Drawing Library”

  1. Hey David,

    nice idea. I’ve worked with Qt too at my last job. I’ll take a look at the code on the weekend, if there’s a way to make it simpler.

    I think Qt is indeed the best choice for C++ GUI programming and you too are right, that Qt likes to do many things “differently”. Especially the signal/slot mechanism and “reflection” can be become very cumbersome for large programs. QtCreator is a nice simple IDE for C++, even without Qt. I prefer it a lot over Visual Studio, which we used at work…

    Regards,
    Tim

    • Thanks! I have another comment on Google+: “I believe you can skip a bunch of the indirection by using QMetaMethod::invokeMethod. Basically, instead of dealing with the connection nonsense, you can generate the queued call yourself. I think that also means you can use something slightly lighterweight than a Q_SLOT, Q_INVOKEABLE or something.”

      Also, I’ve refactored to use QThreads instead of threads, and other Qt stuff instead of chrono and atomic. That seems to help a bit, at least g++ and c++11 are not needed.

      • Now you have it down to as small as it gets, I think. All the macros make my head spin. You might want to split the header and definitions of the receivewidget into two different files and then keep only the macros and the connecting code in an extra file, but all that is only cosmetics…


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: