You are currently browsing the Sticky Bits blog archives for January, 2011.

void main(void)–the argument continues…

January 31st, 2011

For, what must be, years now the perpetual argument among programmers in various forums  resurfaces about the legality, or not, of the use of void as the return type for the main function.

I generally try and ignore these arguments as it seems such a trivial point, but maybe it’s because yet another birthday has just passed it’s time to put my two-penneth in.

Before we start, hopefully we all agree that the following code is an abomination:

main() { }

You would probably be more shocked if I told you that this comes from a fairly recent book (2008) Programming 32-bit Microcontrollers in C. Shame on you all.

The argument against the use of void has weighty backing, notability the creator of C++, no other than Bjarne Stroustrup himself. The following is taken from his very useful C++ Style and Technique FAQ:

The definition

void main() { /* ... */ }

is not and never has been C++, nor has it even been C. See the ISO C++ standard 3.6.1[2] or the ISO C standard 5.1.2.2.1. A conforming implementation accepts

int main() { /* ... */ }

and

int main(int argc, char* argv[]) { /* ... */ }

He then goes on to state;

A conforming implementation may provide more versions of main(), but they must all have return type int. The int returned by main() is a way for a program to return a value to “the system” that invokes it. On systems that doesn’t provide such a facility the return value is ignored, but that doesn’t make “void main()” legal C++ or legal C. Even if your compiler accepts “void main()” avoid it, or risk being considered ignorant by C and C++ programmers

Ouch; ignorant, moi. I take that as a personal insult Winking smile

Unfortunately, what Prof. Strousturp is failing to recognise (or possibly acknowledge) is the hundreds, if not thousands, of embedded programmers out there. For example, in the text above the key line is:

The int returned by main() is a way for a program to return a value to “the system” that invokes it.

Deeply embedded systems don’t have a “system” that invokes it, so there is nowhere for it return to and thus having an int return type is actually misleading.

However, I hear you shout, the standard says it must be; not so.

Both the C and C++ standards define two types of execution environments

  • Freestanding
  • Hosted

To quote the C standard (5.1.2.1):

In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation defined.

and the C++ standard (3.6.1)

It is implementation-defined whether a program in a freestanding environment is required to define a main function

So there you have it your honour, embedded systems are freestanding, and as so I can call the startup anything, therefore void main(void) is perfectly legal.

So I would argue, not understanding that void main(void) is perfectly acceptable in for freestanding environment is in risk of being considered ignorant of the finer details of embedded programming.

I know this won’t be the end of it but next time someone starts down this line, just ask them to explain the difference between a freestanding and hosted environment (makes a nice interview question as well!).

The Baker’s Dozen of Use Cases

January 21st, 2011

Rule 10:  The magical number seven (plus or minus two)

Psychologist George Miller, in his seminal 1956 paper "The Magical Number Seven, Plus or Minus Two: Some Limits on Our Capacity for Processing Information", identified a limit on the capacity of human working memory. He found that adults have the capability to hold between five and nine ‘chunks’ of information at any one time. A ‘chunk’ may be a number, letter, word or some other cohesive set of data.

What has this to do with use cases? One of the primary functions of writing use cases is requirements validation – that is, are we actually building the correct system? The use case model is a technique for presenting the system requirements such that the customer can say either yes, we have a correct understanding of how the system should work; or no, we have misunderstood the system.

When presenting information to our (probably non-technical) customer it makes sense to keep the information content manageable. Try to keep the number of transactions in any one flow (that is, a sequence of use case steps between a trigger event and a conclusion) to between five and nine. This gives your customers the best chance of comprehending what you’re writing.

This means that the use case stops being a list of all the system requirements and becomes a précis of the system requirements. Each transaction may therefore equate to more than one system requirement. In practice this is not as over-simplifying as it sounds: requirements are often ‘clustered’ around elements of data and their production and manipulation – effectively what each use case transaction describes.

The skill in writing good use cases is the ability to précis requirements together to minimise the number of use case steps, without creating simplistic use cases.

The ‘seven, plus or minus two’ rule is not hard and fast; more a guideline. If your description has ten steps, this is not a problem; however, if it has thirty then there’s a chance you’ve over-complicated the step. The same is true at the other limit: three steps is fine; one step means you may have over-simplified and lost detail.

 

<<Prev     Next>>

C++ Overheads

January 14th, 2011

Recently IAR have finally released full support for C++ (adding exceptions and RTTI) to their family of cross compilers. Initially the kickstart (free) version had not had exceptions and RTTI enabled, however with the release of version 6.10.2 this has now been rectified.

We currently use the IAR compilers on our training courses, targeting an NXP LPC2129 (ARM7TDMI) based systems. As part of verifying that the previous version’s (v5.41) projects still work with v6.10, I decided to investigate the potential overheads of full C++ in this environment (I’m pleased to say all projects worked under v6.10 without modification – phew).

Here are my preliminary findings and I’ll add to them as I investigate further:

First off, I created a project based on the C++ main project, giving the following code:

int main()
{
return 0;
}

[Yes I know, return 0 isn’t necessary and it’s perfectly legal to have “void main()” in C++]

First off, the default language selection is still “Extended Embedded C++”, so I needed to change this to full C++. All build numbers are based on a Debug setting and I/O set to semi-hosting (I/O via debugger terminal window).

image

The build then gave (in bytes):

  • code – 296
  • const – 1
  • data – 8704

The data size surprised me, but by checking the default settings in the Linker configuration file, the default stack size was set to 0×2000 (8192). By changing this to 0×400 (1024) the data requirement dropped to 1536 bytes. I haven’t, as yet, dug down to where the other 500+ bytes are coming from (for another day).

Read more »

The Baker’s Dozen of Use Cases

January 14th, 2011

Rule 9 – Build yourself a Data Dictionary

Transactions between the actors and the system typically involve the transfer of data.  This data has to be defined somewhere.  If you’ve built a Domain Model ( see Part 5 – here) most of the data will be identified there; but even then the class diagram is not always the most practical place to capture the sort of information you need to record.

Another way to capture this information is in a Data Dictionary.  A data dictionary defines each piece of data in the system, and attributes about that data.  Typically, a data dictionary holds (but is not limited to) the following:

  • An Identifier.  This is how the data will be referred to.  Remember to use a term that has meaning in the problem domain.
  • Definition.  What does this data represent?  (And remember: a good definition consists of a genus – what type of thing I’m defining – and a description)
  • Units of measure; if applicable
  • Valid range; again, if applicable
  • Resolution.  That is, what’s the smallest difference I can measure?  This can become important if your implementation may use fixed point number schemes (for example).

This information is all vital to the developers who must design and code interfaces, etc. but it also has benefit when writing use case descriptions – it decouples the behaviour of the system from its data.

Just as we should decouple user interface details from the use case description (see Rule 8) so should we decouple the data transactions.  Describing the data requirements of a transaction in the use case description is cumbersome and leads to a potential maintenance problem (what if the resolution of a data item changes?  Or its units of measure?  Ask the team that developed the flight systems software of the Mars Climate Orbiter about getting units of measure confused!)

Data Dictionary

As you define your data dictionary, you can refer to data items (by their name) in the use case text.  Use some typographical convention (I use italics) to identify a data item from the data dictionary; or, you could hyperlink it. 

If the data requirements change you shouldn’t need to change the use case – unless it leads to a change in behaviour.  Similarly, you don’t need to elaborately detail exception flows for invalid data.  The data dictionary defines what is valid; the use case defines what should happen if we receive invalid data.  A simple, clear separation of responsibilities.
 
The Data Dictionary is not a new idea.  Twenty years ago, when I started developing software, a Data Dictionary was always created, and was considered a vital part of the design.  Unfortunately, it seems to have gone out of fashion, or been forgotten, in many software development circles.

 

<<Prev     Next>>

%d bloggers like this: