alt.embedded

C Programmers, Time To Try Ada

If you are a C programmer then you probably heard about Ada but discounted it because it isn't C. The typical view of Ada is that it is only for military applications. There are a host of myths and facts that typically get mixed up when talking about any non-C language. Some are true, most are not.

I started out programming in Basic and spent most of my time working with assembler and C. I've done production work with everything from APL, Algol, PL/I, Turbo Pascal and Cobol just to mention a few. Ada was never on that list but after working with it lately it should have been. It is why I recommend that C programmers take a look at it because you will likely wind up with code that has fewer bugs. It will also be easier to create safe and reliable software compared to using C.

I am going to highlight some areas that I think distinguish the latest incarnation, Ada 2012. These include contracts, multitasking, object and package system. This isn't an intro to Ada. There are other documents that do that much better.

If you want to check out some of the ideas presented here you can take advantage of one of the free Ada development platforms like GNAT available at Adacore's download site. This includes an Eclipse plug-in called GNATBench.

Ada 2012 Contracts

One of the new features in Ada 2012 are contracts. These are essentially conditions that can be applied to function, procedures and type definitions. The C assert macro found in many programmers' toolkits provides similar services but it tends to be non-standard and the functionality is much less than what Ada provides. The follow is a short example of Ada's functional contract support.

pragma Assertion_Policy(Check);
package Stacks is
  type Vector is array (Positive range <>) of Integer;
  type Stack ( Max_Length : Natural ) is
    record
      Entries: Natural;
      Data : Vector ( 1..Max_Length );
    end record;

  procedure Push ( S : in out Stack; E : in Integer )
  with
    Pre => S.Entries < S.Max_Length,
    Post =>
    ( S.Entries = S'Old.Entries + 1 ) and
    ( S.Values ( S.Entries ) = V );

  procedure Pop ( S : in out Stack; E : out Integer )
  with
    Pre => S.Entries > 0,
    Post =>
    ( S.Entries = S'Old.Entries - 1 ) and
    ( S.Values ( S'Old.Entries ) = E );
end Stacks;

 

The pragma indicates that the Pre and Post conditions will be checked. The package is Ada's way code is collected together. The example is the header definition for integer stack that has two procedures associated with it: Push and Pop.

The Pre and Post clauses for each specify conditions that will be met when the procedures start and exit. This provides users of these procedures with information about requirements and limitations of the function. In C or other programming languages, this information have to be done in comments making it non-standard.

Ada's approach highlights the difference between it an other languages like C. In the latter case, something like an assert macro or checks within the code will be found on the implementation side. This tends to be easy for adding a precondition check but doing a post condition check is more problematic since it is usually possible to exit for a number of points within the code. Ada handles this chore within the compiler so it does not matter how a procedure returns.

The references to the original values of variables in the postcondition look like S'Old. This could be done in other languages but usually at the cost of explicitly defining a new variable to save the value. If Ada implements the postcondition at run time it will have to do this but again it is handled by the compiler, not the programmer.

Ada type contracts are slightly different in that they are applied when a variable of the specified type is used. The following are some examples of type constraints.

subtype Even is Integer
  with Dynamic_Predicate => Even mod 2 = 0;

subtype Winter is Month
  with Static_Predicate => Winter in December | January | February ;

The Even type (or Ada subtype) is essentially any even integer value. The Dynamic_Predicate indicates that this would be a runtime check.

The other is an example of a compile time check noted by Static_Predicate. This means that the compiler can make sure that Winter values will be limited to a subset of Month values.

Contracts alone would be a good reason to check out Ada but there are other reasons as well.

Ada Multitasking

One criticism of C and C++ is the lack of multitasking standards. Ada provides a standard multithreading model. Ada can be used with most operating systems but its threading model needs to be supported as well.

The multithreading support is one reason why Ada makes sense for embedded applications. Often Ada's support is sufficient for many applications. It is portable because it is part of the language. It is also simple to use as well as powerful. Check Ada's documentation for the latter. Here is a simple version.

with GNAT.IO;
use GNAT.IO;
procedure TaskExample is
  task type MyTask ( TaskName : String ) is
    entry Start ;
    entry Stop;
  end MyTask ;

  task body MyTask is
  begin
    accept Start ;
    loop
      select
        accept Stop ;
        exit ;
      else
        put_line ( TaskName ) ;
        delay 0.0 ;
      end select;
    end loop;
  end MyTask ;

  -- Task variable allocation
  Task_1 : MyTask ( TaskName => "Task 1" ) ;
  Task_2 : MyTask ( TaskName => "Task 2" ) ;

begin
  Task_1.Start ;
  Task_2.Start ;
  delay 0.01;
  Task_1.Stop ;
  Task_2.Stop;
end TaskExample;

 

Two identical threads are defined, started and then stopped. They simply print their name until they are terminated. There may be any number of entry points and arguments can be included as well.

Many embedded applications can be handled by this basic approach. More complex applications can be created using Ada. The nice thing is the Ada application will be portable.

Ada Objects

C lacks object support and C++ makes radical syntax changes to support objects. C++ has a very powerful template system and it supports generic functions. Of course, moving to object oriented programming (OOP) with C++ is a major change.

Ada actually supports a wider range of object oriented techniques than C++. Ada supports classes, inheritance and polymorphism. This includes multiple inheritance as well as mixin inheritance.

The big difference between C++ and Ada's object oriented approach is that Ada does not make the major syntactic changes that C++ requires. Instead it builds on the existing package and record definitions. It also uses an overloaded, generic function approach similar to that found in other languages like CLOS (Common Lisp Object System).

Ada can simply uses conventional function or procedure conventions when calling methods associated with an object instead of prefixing the method with the object reference as in C++. For example:

// C example
MyObject.Method1 ( Param1 ) ;

-- Ada example 1
Method1 ( MyObject, Param1 ) ;

-- Ada example 2
MyObject.Method1 ( Param1 ) ;

 

Unlike C, Ada's Method1 is a generic function that is matched based on the argument types. Of course, Ada can also use the C++ prefix method as well as noted in the second example. The two Ada examples are equivalent so migrating from C++ is not as difficult as one might guess.

Ada's class hierarchies come into play when matching function definitions. One advantage of this approach is an easier migration of a non-OOP application to an OOP approach.

A full example gets a bit more involved than what I want to present here but there are plenty of examples and explanations to be found online. Just search of Object Oriented Programming Ada.

Ada Package System

Ada's package system is similar to Java but I think that Ada's is a bit more sophisticated. There are matching definition and implementation files. Ada also handles namespaces better. C++ introduced the concept namespaces but not all compilers support it.

Ada packages support nested packages, child packages and subunits. These features makes incremental package management and development easier. Like many Ada features, the package system has lots of power and flexibility when needed but it is simple to use when getting started.

Ada Tools

The number of Ada suppliers is smaller than C and C++ suppliers but there are plenty available. Many Ada vendors provide a range of tools. For example, Green Hills Software sells Ada, C and C++ compilers and development tools. Atego sells Ada tools along with Java and UML. Other vendors include DDC-I, IBM Rational and MapuSoft Technologies.

I have not had a chance to check it out but Adacore has support for Atmel's AVR. There is also a Zero Footprint Profile designed for embedded applications. It contains no references to the GNAT library that itself is a stripped down version designed for minimal overhead.

Ada actually plays nicely with most programming languages including C. This makes utilization of legacy libraries relatively easy.

Ada will solve all problems but it is likely to be a better tool for most embedded applications. I am not an Ada maven. Actually I am more of an Ada newbie but it is a tool that I will be using a lot more. The only way you will know is to try it.

Resources

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish