ARTiSAN Logo Prodotti     Download     Centro UML     Società     Clienti     Contattaci     Home

Novità     Eventi     Partner     Lavoro     Training     Supporto     Mappa Sito

Fornitore Leader di strumenti software di sviluppo UML tecnologicamente avanzati

[Sito in lingua inglese]
English

Novità ed Eventi

Eventi
Sala stampa
Datasheet
Articoli
Libri consigliati
Home
Mappa Sito
Contattaci
Mailing List
Parla di noi a un amico

Code Generation From UML For Small Systems

Published by Embedded Systems Engineering, UK, May 2000

A large proportion of real-time/embedded development, even today, is still targeted at small systems with 8/16 bit processors and limited RAM and ROM. As the complexity of software development for small systems continues to increase engineers, who previously were content with informal structured design techniques, are investigating the use of UML, the emerging standard in object-oriented design techniques. And as time-to-market issues achieve ever-greater focus, they are also keen to gain from the productivity offered by generating code from UML design models. However, for a majority of such engineers, three problems quickly emerge:

They often need to use C, for all or a significant part of the system software, either for efficiency, or because of the availability of tools for their target environment;
They don't have the time or desire to make significant changes to their current development process, in order to use new tools;
They need to be able to write efficient code, often at a very low level.

This article shows how Round-trip Engineering between UML and C can be employed to enable engineers to benefit from the productivity and quality improvements on offer, but without imposing significant development and runtime overheads.

Firstly, we'll provide an introduction to OO, and then we'll discuss the three issues raised above.

A Brief Introduction to the OO Approach

This section highlights a few of the key concepts underlying OO analysis & design techniques. It also discusses the benefits to be derived from the use of such techniques even, as we will see, if the target language will not directly support all the OO concepts:

Abstraction - An OO approach encourages the construction of abstractions, both of the real-world of the system (sensors, motors etc.) and of the problem (making calls, planning routes). The basic notion of classes of similar objects (often called instances) provides a natural way of dealing with multiplicity in the problem (e.g. many simultaneous phone calls). Data can either be statically defined against the class, or can be related to each instance. Functions either operate at a class level, or on individual object data. The concept of object relationships allows the structures in the problem to be replicated in the solution;
Consolidation of Common Functionality - An OO approach simplifies the identification of common functionality that can be consolidated. This will lead to smaller, more maintainable and higher quality systems. The ability to abstract out common functionality reduces code size by avoiding code duplication, thereby reducing the maintenance cost and simplifying the testing of the developed system. It also produces a simpler design that is easier for other engineers to understand;
Encapsulation / Information Hiding - OO techniques provide a more obvious and natural mechanism to limit access to shared data by encapsulating or hiding information. OO provides a single construct (the class) that wraps up both data and functionality and only 'exports' a necessary subset as its 'public' interface, keeping the rest 'private'. This approach minimizes the impact of requirements changes and reduces the risk of ill-disciplined or "abusive" implementations. It also reduces the complexity of developing very large systems;
Inheritance & Components - Inheritance is a mechanism provided by OO approaches to enable class refinement & reuse. Reuse is achieved by 'inheriting' data and functions from one class into another. Component identification leads to a much higher level of reuse. Typically, a component would be a collection of related classes, which have been packaged together to provide some high level functionality that is likely to be reused across multiple projects.

Three Issues

When engineers working on small systems consider the move towards OO and modeling tools certain issues, as discussed in the introduction, are always raised. In this section we'll elaborate on those issues a little and then show how Round-trip Engineering from UML to C helps address them.

Can UML be used when C is the target language?
Many engineers building software for small embedded and/or real-time systems use C, either because of tool support, for example compilers, concerns about efficiency (see below) or skill set. In fact a recent survey of visitors to our Web site, irrespective of system size generated the following statistics regarding language use (we would expect the use of C to be more pronounced in small systems development):

C/C++ 42%;
C/C++/Java 31%;
Only C++ 10%;
Only C 17%.

This section suggests practical strategies for mapping your OO design to the C language, in particular suggesting a pragmatic subset of the OO behavior normally provided by an OO language. The result is that the essence of an OO design can be preserved even when implemented in a procedural language, enabling the developer to derive many of the benefits of an OO design approach.

There are a number of features which OO languages and development tools provide. When implementing an OO design in a procedural language these will not be available and therefore some constructs need to be replaced or emulated, or simply avoided. This will simplify the implementation and ensure that the design can continue to be kept in step with the resulting code. By following the guidelines outlined here you can preserve quite a few of the key features of an OO design.

Figure 1. Example of UML to C Mapping.

Figure 1 shows a UML representation of a class (Class1) with a variety of features:

A private class attribute, indicated by the "-" sign, which occurs once per class;
Two instance attributes (one public, one private), which occur once per object (instance of the class);
A private class operation, which applies across all instances of the class;
A public instance operation, which can be invoked on any object;
A reference to another object (called a role).

The basic mapping creates two files per class, the header (.h), which is used by other classes to gain access to 'public' data and functions, and the body (.c), which contains the function implementations and any class (static) attributes.

The mapping is as follows:

Public features appear in the header file;
Private features don't occur in the header file;
Instance operations have a standard first parameter, called 'this' which passes in a reference to the struct for the object data. This is similar to the handle scheme used in many other situations;
Class operations are just normal functions;
All of the instance attributes are combined into a struct; (note that private instance attributes are declared in the header file, as part of the struct for the instance - it's hard to implement an elegant mapping to cover this - a safe mechanism is to provide functions to access and change attributes (often called mutators and accessors) and only use those - these functions can then be made public or private as required.
Class attributes are declared at file level; private class attributes are not visible in the header file, and are marked as static in the body so that they are hidden from the linker; public class attributes are declared bare in the body file, and marked as extern in the header file so that the users of the header file do not make space for that attribute.

In this mapping we haven't accommodated:

Instance construction and destruction - You need to write your own functions to allocate and deallocate memory for the instance structs;
Inheritance and Dynamic Binding - There are ways of implementing these features in a non-OO language, but we wouldn't recommend it with the round-trip engineering approach suggested here, because the clear mapping between code and model is lost. An alternative to inheritance that can be used relies on delegation. Using this mechanism, if a class offers a set of functions, some of which are 'inherited' in another class; when the 'descendant' class is asked for one of these, it delegates to the 'ancestor' class, by calling its equivalent function. Transformation from inheritance to delegation is possible to automate and some modeling tools will do this for you.

These two suggested restrictions remove some of the advantages of using an OO design approach, but make the model/code relationship more straightforward. [1] provides more guidance on how to implement inheritance and other advanced OO features in C.

Since some of the stronger typing and additional checking provided by C++ compilers would not be available in C, you may want to take other steps to ensure the correctness and robustness of code. For example:

Use specific typedefs for everything (especially for function pointers);
Avoid casts where possible;
Always test that parameters are valid before using them;
Use enums to replace 'const int' and 'bool', etc.

Can a UML modeling tool be introduced that delivers business benefit, without excessive intrusion on the engineering process?
Small systems are usually developed by small teams, often to tight deadlines, which leads to concerns about the overhead of introducing modeling tools. Our user survey found the following reasons against adopting modeling tools:

25% - Cost/Benefit of such a tool for their project;
27% - The degree of process intrusion that using such a tool might entail.

Again, for smaller projects, we would expect both these results to be more pronounced.

Round-trip engineering is an approach that provides benefits without a large degree of process intrusion.

An Introduction To Round-Trip Engineering

There are various 'flavours' of round-trip engineering; the one shown in Figure 2 is sometimes called 'synchronization' and is the approach favoured by ARTiSAN. Model and code are developed in parallel, and at chosen points a model derived from the code is compared to the design model. Any changes are highlighted and the engineer can choose whether to update model or code if differences are found. Round-trip engineering to UML works most effectively on the code structure (function signatures and data structure definition) rather than function bodies, and this is the approach assumed in this paper.

Figure 2. Synchronizing Model and Code.

Minimizing Process Intrusion

The proposition underlying Round-trip Engineering is to get optimal leverage from both traditional code-based tools and modeling tools, and so minimize the effect (although not the benefit) of using a modeling tool on your development process.

It respects the engineer's need to use a code viewpoint, during intense edit/compile/debug sessions for example, whilst making available a more abstract viewpoint during initial design and subsequent design reviews. Using this approach, either or both model and code can be changed simultaneously and at a convenient point both can be made consistent with each other.

The mapping between model and code is kept very straightforward, so that the round-trip engineering tool can perform this 'synchronization' correctly. This has the side effect of making it very easy to visually relate model and code when required, but imposes the requirement on the engineer to keep to a defined mapping, for example the one above.

Productivity and Quality Improvements

The aim of code generation is to increase productivity and quality in software construction by automating some of the construction process. It achieves:

Productivity, both during initial generation and during refinement and maintenance, by removing some of the more mechanical parts of the coding process;
Quality, by assuring conformance to specification, freedom from some types of construction defect, and general uniformity and consistency, for example consistent function headers, and correct include directives.

Will using a UML-based approach lead to inefficient code?
Given the limited hardware resources available in many of these small embedded systems, it is very important to write code that is efficient. This often results in the use of C, which is seen to be an efficient programming language. It also requires that tweaks to the code must often be made at a very detailed level.

Being able to use an OO approach for C means that engineers can start to use aspects of UML in an 'object-based' fashion, but still continue to derive the efficiency benefits of using C. Furthermore, following the philosophy of round-trip engineering does not impair the ability of the engineer to write efficient code, providing a mechanism for consistent production of code frameworks from a design, but not introducing runtime overhead.

Conclusion

Many engineers building small embedded systems are investigating the use of UML and UML based modeling tools on their projects. An object-oriented design technique, such as UML, offers a number of techniques, such as abstraction and encapsulation, which can help to provide better structured, more extensible and maintainable systems. Using a UML-based modeling tool makes it easier to keep designs current, both during initial design, and often more importantly during maintenance.

However, they have to deal with the issues of efficiency, use of C and process overhead. This paper suggests that round-trip engineering from UML to C, based on a straightforward usable mapping will provide both productivity and quality improvements with minimal intrusion.

Real-time Studio, ARTiSAN's UML modeling tool, offers round-trip engineering of 'C', based on the ideas we have discussed above, plus many other features, for example document generation and more advanced features such as simulation.

 

Copyright © 1998-2000. ARTiSAN Software Tools, Inc. All Rights Reserved.