How do you pick a programming language? Here's some thoughts on C++.
Published in ESP, June 1995
|For novel ideas about building embedded systems (both hardware and firmware), join the 25,000+ engineers who subscribe to The Embedded Muse, a free biweekly newsletter. The Muse has no hype, no vendor PR. It takes just a few seconds (just enter your email, which is shared with absolutely no one) to subscribe.|
By Jack Ganssle
How do you go about selecting a language for an embedded application? Each has it's ups and downs: APL's conciseness comes at the cost of being infinitely cryptic. Forth's extensibility requires a tremendous amount of documentation to back up the meaning of each word. Ada's complexity makes a programmer focus on syntax to the point where, if the program compiles, it will probably run. (Ada, however, seems to be in the early stages of becoming a Latin - a dead or dying language spoken only by a few high priests).
In the embedded world the language debate is largely between C and C++. The magazine pages are crammed with ads from compiler companies offering the latest in OOP products, yet my informal surveys indicate that few in this industry write real C++ code. A recent debate in comp.realtime seems to confirm this observation.
Though C++ scares me (are the compilers be reliable? can we find programmers who really live and breathe it?), I see it as a critical step in promoting software packaging. And, I do believe that reusability is the only reason for migrating to C++.
Someday our mantra will be "inheritance, encapsulation, polymorphism". Why rewrite the same routines time after time? C++, and a strong armed programming discipline, just might make the concept of reusable packages a reality.
I believe that reusable software is our only hope of dealing with increasing software complexity. Let's face it: at some point our own code will be simply too convoluted for anyone, including ourselves, to understand. It's naive to expect that, by itself, a new language will create some magic ability to handle bigger systems.
Why is it so much easier to design an embedded system's hardware than its code? Though programming surely is hard, don't forget that your 30,000 lines of code might be running in a system with over 200 million transistors. Which is more complicated? Which would you expect to be harder to design?
In fact, the hardware community is much better than we are at defining, and living with, levels of abstraction. No one designs a computer for an embedded system. We use an off the shelf processor. Need some memory? Pick one of thousands of standard chips. I/O is just as simple - hundreds of different kinds of peripherals lie at our fingertips, with yet more hundreds of variations of each type. Any one chip might be supremely complex, but each is easy to use.
Programmers decompose a problem into its functional blocks and then laboriously code each block. The hardware engineer also partitions the problem into functional blocks. Then, he uses a few standard parts to implement that section.
The problem of hardware complexity has been mastered by standard parts and a will to use them, even if the design must be altered to fit the available parts. Software folks simply must reach the same mastery of complexity to deal with the larger programs looming in the future.
Every chip comes with a well defined interfaced bounded by some number of pins. Voluminous data books describe the action of the device in gory detail. While the unit's internal operation might be glossed over, its interface is well defined with timing diagrams, input and output levels, and the like. Doesn't this sound a bit like the OOP nirvana we're all seeking?
C++, by itself, will not create massive software reuse. It's but one ingredient in a larger recipe. Just as knowing how to use Corel won't make you a graphic artist, the tool itself can greatly aid in achieving an objective.
Programming is like accounting. It's not an end in itself; it's merely one necessary function performed by a company in pursuit of profits. Select a programming language that fits the organization's goals and objectives; not one that just fits your own style.
Though we're technologists infatuated with technology, don't pick a lingo solely on technical merits. Poor language selection can dramatically effect a company's viability, so make sure your choice is based on sound business thinking.
Can you get competent programmers familiar with the language? Will you be able to get people with this knowledge way out at the project's projected end of life? The latest fad could be tomorrow's bit of history, leaving the company in dire straits when looking for someone to make a few patches.
This sort of implies that a "safe" strategy is to follow the herd. If the whole world is using Modula 2, then Modula is probably a safe bet in terms of finding people to do the work. Surely, C fits this bill now. I would bet that C++ will have a large enough installed base in a few years that plenty of adept people will be available.
The management ranks in high tech organizations often suffer from a fear of change - an appalling thought considering that most of the whirlwind of societal change we've seen in the last generation springs from the electronics industry. A fact of software engineering is that relatively young engineers quickly find their way into mid-level roles of responsibility, and quickly start losing touch with state-of-the-art technology as their jobs require more personnel work and less techie stuff. A manager comfortable with assembly will naturally encourage his folks to use assembly, and will be a bit afraid of any language of which he is not a master. This slows the adoption of all sorts of new technologies, languages being one. Perhaps there is a benefit to this, as the inertia helps avoid erratic pursuit of new fads. Surely there's a lot of evil as well.
The C versus C++ debate is especially sensitive to this. You don't gain the benefits of object oriented programming without adopting a new way of looking at solving software problems. C is more like assembly, at heart, than C++ is like C. It's hard to make the inductive leap an OOP requires. As a middle-aged manager, I know!
But, the manager's role is to insure that his people are doing the right things, in addition to doing things right. Picking a language is taking a leap of faith into the future. Select one that builds the company's core competencies, that stretches the team in the direction that the organization should be headed.
The only constant is change, so you've got to develop a strategy that elegantly waltzes with change rather than resists it. The clever manager never views a project as an end in itself. Though the immediate goal is generally nothing more than building a product, you've got to use each and every engineering effort as a training camp for every member of the team. Move the team's competencies ever forward, always gaining experience as well as a product.
Now, an assembly-only shop should probably not make a sudden leap to 100,000 lines of C++. You have to balance the risk of being late with the need to try new things. Constant incremental improvements are good. Radical surgery is something a company can tolerate only occasionally.
We can all learn from consultants McKinsey & Company. They'll turn down dead-end jobs that bring no more benefit for the company than cash. Projects are selected and planned to insure that each one improves the business's competencies. This is long range planning and investment at its best!
Every company needs a certain amount of leading edge work designed to push the envelope. Mitigate risk by limiting the scope or importance of these projects, but do try different things. Even if your group has no use for C++, run a small effort using it to increase the skill levels of your workers, and to learn important new things.
Now, going back to a comment I made earlier, I believe the primary reason to use C++ is for software reuse. Period. C++ has all of C's wonderful ability to control any bit in the system, yet comes equipped for serious encapsulation and subsequent routine reuse. Go to C++ if reuse is your god. This means that everyone involved is committed to code recycling, from the CEO to the janitor. If your manager is not beating a reuse drum, keeping the entire team's vision focused on this critical goal, then reuse will be abandoned the first time a ship date nears.
Don't assume management has your best interests at heart. If you are caught in an intellectual backwater, work hard outside of the business to stay up to snuff on all of the latest technology, since today's whiz bang idea will be the base level of competition tomorrow. Any engineer who stops aggressively learning and experimenting condemns himself to technical obscurity.
The holy grail of language and compiler debates is efficiency. How fast is the compiled code? How big is it? Software folks love to debate how one runtime package searches strings a few microseconds faster than another.
Select a language (and compiler) based on your real size and speed constraints. Is it efficient enough for your application? When the crummiest compiler in the world is perfectly adequate, don't torment yourself with debates over unimportant microseconds.
In a high volume tiny application, like the PIC-controlled blinking lights on kids' sneakers, assembly language may be the only appropriate choice. Larger projects, especially those produced in small quantities, should be language insensitive: crank up the clock speed or get a larger CPU in the interest of reducing software development costs.
Counting T states is a terrible waste of a programmer's mind. Avoid it in any but the most cost-sensitive applications.
Modern C and C++ compilers generate high quality code. Trust your compiler. Most overhead problems come from weaknesses in the processor's architecture, like poor stack handling abilities. Recently I saw this graphically when comparing real time trace data on a Z80 to the original C source: ++I (with I a 16 bit signed integer) generated about 25 instructions. Changing I from an automatic to a static variable cut out 75% of the overhead.
Crummy code problems come mostly from programming without thinking. Don't force the compiler to optimize your program for you. Fold constants yourself, remove unneeded code from loops, and minimize conversions between types. This is just good programming style.
Interrupt and Device Handlers
Select a language that is complete enough, and efficient enough, to handle all aspects of your task, including real time interrupt handling. Sure, there may be one or two ISRs that simply must be coded in assembly, but remember that assembly is a productivity disaster.
C compilers are now so efficient that I recommend writing all but the most critical ISRs in this language. Assembly is simply too tedious, and linking assembly to C is sometimes an art - particularly if you have to pass data between languages.
Be sure your compiler has built-in support for writing ISRs. We've found that even Microsoft and Borland's DOS compilers do a great job in interrupt intensive embedded systems. True embedded compilers, like those from Software Development Systems, Microtec Research, Avocet, and a host of others all include ISR-specific keywords.
The compilers will take care of pushing, popping, reenabling interrupts, and the return. Frequently the linker will even set up the vector table for you as well. A few low-level OUTs, issued from your C code will program the external controllers and other devices with the proper vectors.
If your tentative language choice is a loser in the real time segments of code, reject it. You don't use COBOL in embedded systems; why pick some equally inappropriate lingo for your application?
Sometimes you just can't make a rational language decision. All too often the contract mandates details down to the toolchain. It's noble to wage a holy war against the use of FORTRAN-60, but be prepared to lose gracefully. Swallow hard, search your soul to see just how badly you want to stick with that job, and get the work done or start circulating resumes.
Never forget that you can write crummy code in any language. Conversely, no mainstream lingo will prevent you from writing software as beautiful as that crafted with any other tool.
I do believe we'll all eventually migrate to C++, but am afraid that the driving force will be language lust (the "this is the new cool thing" factor), not a commitment to recycling code. Yet you can make a choice today to start reusing code, no matter what language you're working in. Your productivity increase will earn you fame as the master software guru... and maybe even some fame and riches.