Channels ▼

Al Williams

Dr. Dobb's Bloggers

C++ Mysteries

July 10, 2012

I'll confess. I like coding in C. It isn't that I don't understand the benefits of C++ and other more modern languages. It isn't even that I don't use many of those other languages where I am required to or when the benefit is overwhelming. But I enjoy C (and assembly, for that matter).

This could be just another facet of my curmudgeonly nature. After all, although I have a lot of real and virtual calculators, I also have a collection of slide rules that I still use. Of course, I don't think my predilection for C is as unusual as my slide rule collection.

On the other hand, I'm a realist. A very smart programmer once told me that they couldn't use C++ for some embedded code because "C++ just puts things in your code that you don't know about." I thought he was a pretty smart guy, so I'm assuming that was a form of hyperbole. But I do believe that sometimes people think C++ compilers engage in black magic and add mysterious and unknowable things to your code.

Of course, this is ridiculous. If the compiler can generate it, you can understand it. In fact, the first C++ compilers were really front ends that produced C code you fed to the C compiler. So there's no hidden magic nor is there any randomness that would prevent you from figuring out what's happening.

What does C++ add to your code at runtime? One thing to keep in mind is that there is very little difference between a class and a structure. In fact, in C++ they are identical except for the default visibility of members (structure members are public by default and class members are private). Defining a structure and a bunch of functions that take pointers to the structure as their first argument is not very different from writing a C++ class. In C++, of course, that first argument is the hidden this pointer, and that's one of the magic, hidden things the compiler does.

The compiler also manages a virtual function call table if (and only if) you use virtual functions. That means there is an extra pointer in your class (or structure, if you have a structure that contains virtual functions) that points to a block of memory with the addresses of the virtual functions.

Of course, the compiler may also create default constructors and assignment operators for you, but only if you fail to create them yourself. Besides that, the rules it uses to do this are well understood. Other "mysterious" things are really just features. So sure, basing class X on an existing class Y will bring in members from Y (and any base classes of Y). It is supposed to!

Redefining operators is another source of confusion. I'd like to get a T-shirt that says, "I saw cout being shifted left hello world times and decided to drop the class." Again, you don't have to redefine operators and if you do, presumably you understand what you are doing and make smart choices (don't define a + operator to do subtraction, for example).

The irony is that many C programmers roll their eyes at other language proponents, because C is replete with tricky features like dynamic memory allocation and the preprocessor. Both of these examples are powerful tools, but require some knowledge and discipline to work well. I'd submit that the "magic tricky features" of C++ fall into the same category.

Even though I enjoy writing in C myself, I have become a great believer in the use of C++ to build tools. This is especially true in larger projects. I can build a toolset in C++ and, with the addition of some code generation tools, use developers who are mostly coding traditional C code.

You can see this sort of design in popular class libraries like Qt or (if you are a bit older) MFC. The average user of these frameworks will never have to manually derive a class or write member functions. They just fill in "super" C code into predefined spaces. You might use some C++ features if you want to, but you can do quite a bit with only a superficial understanding of the differences between C and C++.

I often think of how these frameworks operate as "design by difference." That is, the framework provides a basic application that does all the things you expect (shows a window, has a menu, properly manages resources, etc.). Your job is to provide the differences between a generic application and your program.

After working on some larger Linux-based embedded systems, I've been walking through my library of assembly code looking for things I want to pull out as more portable C or C++ code. One framework I've used frequently manages the classic LCD display with a few buttons for a rudimentary menu-based user interface.

I've used variations on this code for years, but it was always tied to one processor or another and I invariably was using a processor I didn't have already built. Using C or C++, I can produce portable code. In fact, I wanted to make a C++ framework that uses the design by difference philosophy.

Check out the resulting library. I didn't provide any bindings for specific embedded systems today. Instead, there is an example test harness that uses Linux and simulates the input and output in a text console.

The core to the library is a single class, lcdui. This is a prototype LCD-based application that can handle several different kinds of menu items:

  • T_ACTION — Causes a callback with a code so you can execute some function
  • T_MENU — Invokes a submenu when selected
  • T_INT — Increments or decrements an integer value by a fixed step value (and within limits)
  • T_ENUM — Selects one of several options

The user interface is typically four buttons: up, down, enter, and back. Up and down select menu items or alter integers or enumerated items. Enter selects an item (or ends editing) and back backs out of editing or a submenu.

Of course, the framework can't know how you have the LCD interfaced or how your buttons are connected. That's why it has several virtual functions to handle input and output. Here's the class definition:

class lcdui
{
 protected:
  MENU *menu;   // the current menu
  unsigned int current;  // current index in the menu
  
  public:
  // constructor
   lcdui(MENU *_menu) : menu(_menu) { current=0; };
  // execute the menu
  virtual void go(unsigned int menulevel=0);
  virtual void callback(int id, MENUTYPE mtype, EVTYPE event, int *value=NULL);
  // override to handle T_ACTION menu items
  virtual void dispatch(int id) = 0;
  // called when no input available
  virtual void idle(void) {};
  // get an input code
  virtual INTYPE  getInput(void) = 0;
  // Write to the LCD
  virtual void output(std::string &ostring) = 0;
};

The last two entries handle the input and outputs. The idle, dispatch, and callback functions let you customize how things work (normally, you'd only need the dispatch function for a simple system). The key, of course, is the array of MENU structures. Here's my test setup from linuxdemo.cpp (available from the code repository):

// Main menu with some of each type of menu
MENU mainmenu[] =
  {
    { "Entry 1", 1, 0, T_ACTION, NULL, 0, 0, 0, NULL, NULL, 1  },
    { "Entry 2", 1, 0, T_ACTION, NULL, 0, 0, 0, NULL, NULL, 2  },
    { "Entry 3", 1, 0, T_ACTION, NULL, 0, 0, 0, NULL, NULL, 3  },
    { "Value", 1, 0, T_INT, NULL, 0, 10, 1, &value1, NULL, 1   },
    { "TF", 1, 0, T_ENUM, NULL, 0, 0, 0, &tfval, truefalse, 1   },
    { "SUBMENU", 1, 0, T_MENU, menu2,  0, 0, 0, NULL, NULL, 1  },
    END_MENU    
  };

END_MENU is just a macro with a NULL entry. The program defines several other items to finish out the menu:

// Simple sub menu */
MENU menu2[] = 
  {
        { "Entry 4.1", 1, 0, T_ACTION, NULL, 0, 0, 0, NULL, NULL, 41  },
        { "Entry 4.2", 1, 0, T_ACTION, NULL, 0, 0, 0, NULL, NULL, 42  },
	END_MENU
  };

// Values we want to remember */

int value1=5;  // simple integer
int tfval=0;   // true/false enum


// Enumeration for tfval
ENUM truefalse[] =
{
  { "FALSE"  },
  { "TRUE" },
  { NULL   }
};

To make the system work, you need to subclass the base class and provide (at least) the pure virtual functions (those that end with =0 in the header, namely getInput and output). The example program names the subclass linuxdemo. Then a simple main kicks off all the logic:

// Main just creates our subclass and go
int main(int argc, char *argv[])
{
  linuxdemo program(mainmenu);
  program.go();
  return 0;
}

Could you have done the same thing in C? Of course. Would it be as elegant (however you define elegant)? I don't think so. Admittedly, there is some overhead involved in this setup. Some of it was due to design choices (for example, the wasted memory in the MENU structure). Some of it is due to virtual functions, no doubt. Then again, is an extra function call in an LCD menu function a big deal? Probably not.

Next time, I want to talk more about virtual functions in embedded systems and look at the LCD library a bit more. Meanwhile, you can grab the latest code from the repository (and I won't promise I won't update it) and experiment with it.

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Comments:

rudmerriam
2012-07-31T20:17:02

Not disagreeing at all since I've been doing C++ since the early 90s.

I did want to point out that macros, malloc (and realloc...), free, etc are even more tricky than new and other C++ constructs.


Permalink
ubm_techweb_disqus_sso_-2a71e893a4277324fad958e9923e2620
2012-07-25T00:35:43

Maybe you slightly misstated the person's comments (or maybe that person did). Instead of "C++ just puts things in your code that you don't know about" maybe more accurately "C++ puts things in your code that you really must know about or you're screwed." To me, the chief among the extended hazards to watch out for: constructors, operator new, exceptions, and templates. If you don't know exactly what you're doing with these, and how the compiler uses them, there are so many ways to go wrong.

I should point out that C++ is my primary everyday computer language. If I want the relative simplicity of C, I can code C++ in a straightforward C style -- using only as much complexity as I need, and what I don't use doesn't hurt me. From time to time, I have used all of the "warning flag" features above with care and with success.


Permalink
ubm_techweb_disqus_sso_-b51f7a869a1175412e56da18be4810cb
2012-07-12T11:37:24

Yes, that's it. So the idea is an experienced C programmer looking at "Hello World" written in C++ might read the line:

cout<<"Hello World";

As: cout shifted left hello world times. As ARLIU says, a joke ;-)


Permalink
ubm_techweb_disqus_sso_-8e3ceb85197a547fc34f1b9777cad9eb
2012-07-12T09:12:47

It is a joke about redefining operators. << is redefined.


Permalink
ubm_techweb_disqus_sso_-6d7354976031e54f1d4924096a96c678
2012-07-12T04:12:35

What do you mean "I saw cout being shifted left hello world times and decided to drop the class"? I don't get it.


Permalink


Video