Design Considerations For Using C++ in Safety Critical Avionics Systems

Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 23

Design Considerations for Using C++ in Safety Critical

Avionics Systems

Prepared by:
Mike Peuser
Principal Software Engineer, APEX Platform Team

Approved by:
Rich Bontrager
Team Lead, APEX Platform Team

Approved by:
Tom Roth
APEX Certification Coordinator

Approved by:

SQA Representative

Approval Date:

FAA/APEX White Paper 721-00887-0001


FAA Project Number: SP3551WI-A

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 1 of 23
Honeywell International Inc. PROPRIETARY
This document and all information and expression contained herein are the property of Honeywell International Inc., are
loaned in confidence, and may not, in whole or in part, be used, duplicated, or disclosed for any purpose without prior
written permission of Honeywell International Inc.

Copyright 2003 Honeywell International Inc. All rights reserved.

TRADEMARK NOTICE
BENDIX/KING and APEX are trademarks of Honeywell International Inc.

Honeywell International Inc.


One Technology Center
23500 West 105th Street
Olathe, KS 66061

REVISION HISTORY

Revision PRN/CO By Date Modification


– 728682 M. Peuser See Cover Initial Release

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 2 of 23
TABLE OF CONTENTS

1 INTRODUCTION.............................................................................................................................................................5
1.1 Scope............................................................................................................................................................................5
1.2 Referenced Documents................................................................................................................................................5
1.2.1 Technical...............................................................................................................................................................5
1.2.2 Industry.................................................................................................................................................................5
2 C++ FEATURES WITH ISSUES....................................................................................................................................6

3 CONSIDERATION OF C++ FEATURES WITH ISSUES..........................................................................................8


3.1 Function Overloading..................................................................................................................................................8
3.1.1 ISSUE: Structural Coverage.................................................................................................................................8
3.1.2 ISSUE: Function Selection...................................................................................................................................8
3.2 Classes..........................................................................................................................................................................9
3.2.1 Compiler Generated Code.....................................................................................................................................9
3.2.1.1 ISSUE: Compiler Generated Constructor/Destructor Invocation..................................................................9
3.2.1.2 ISSUE: Potential Compiler Generated Functions........................................................................................10
3.2.1.3 ISSUE: Global Object and Static Object Construction/Destruction Functions...........................................10
3.2.1.4 ISSUE: Compiler Generated Dynamic Dispatch for Virtual Functions......................................................11
3.2.2 Inheritance (Single and Multiple).......................................................................................................................13
3.2.2.1 ISSUE: Possible Dead/Deactivated Code....................................................................................................14
3.2.2.2 ISSUE: Possible errors due to methods and attributes inherited via multiple paths....................................14
3.2.2.3 ISSUE: Data and Control Coupling (and Flow)..........................................................................................16
3.2.3 Polymorphism (Virtual Functions).....................................................................................................................18
3.2.3.1 ISSUE: Dynamic Dispatch Structural Coverage.........................................................................................18
3.2.3.2 ISSUE: Function Selection..........................................................................................................................18
3.2.3.3 ISSUE: Possible Dead/Deactivated Code (due to function overrides)........................................................18
3.2.3.4 ISSUE: Constructor/Destructor Virtual Function Usage.............................................................................18
3.2.3.5 ISSUE: Data and Control Coupling (and Flow)..........................................................................................19
3.2.4 User Defined Implicit Type Conversions...........................................................................................................19
3.2.4.1 ISSUE: Loss of Data....................................................................................................................................19
3.2.4.2 ISSUE: Function Selection..........................................................................................................................19
3.2.4.3 ISSUE: Data and Control Coupling (and Flow)..........................................................................................20
3.2.5 Encapsulation......................................................................................................................................................20
3.2.5.1 ISSUE: Data and Control Coupling (and Flow)..........................................................................................20
3.2.6 Object Lifetime...................................................................................................................................................20
3.2.6.1 ISSUE: Global Object Construction Order..................................................................................................20
3.3 Inline Functions..........................................................................................................................................................21
3.3.1 ISSUE: Possible Dead/Deactivated Code (branches).........................................................................................21
3.4 Templates...................................................................................................................................................................22
3.4.1 ISSUE: Structural Coverage...............................................................................................................................22
3.4.2 ISSUE: Parameter Selection...............................................................................................................................23
3.4.3 ISSUE: Possible Dead/Deactivated Code...........................................................................................................23

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 3 of 23
LIST OF FIGURES

Figure 1 Virtual Function Example.....................................................................................................................................11


Figure 2 Virtual Function Interface Strategy.......................................................................................................................12
Figure 3 Reuse via Inheritance.............................................................................................................................................14
Figure 4 Redefinition via Multiple Paths.............................................................................................................................15
Figure 5 Data and Control Coupling Example.....................................................................................................................16
Figure 6 Data and Control Coupling - Flattened..................................................................................................................17

LIST OF TABLES

Table 1 Call Sequence for Base::run method.......................................................................................................................16

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 4 of 23
1 INTRODUCTION
Every programming language goes through an adoption process where users familiarize themselves with the language
and discover the strengths and weaknesses of the language. Many users of a language establish coding standards or
usage guidelines for the language. These standards are driven by a blend of concerns including portability,
maintainability, readability, best practices, safety, and reliability.

The C++ language has undergone this adoption process within the majority of the user community. It has been used in
several safety critical products, but many groups either use C++ as a better C by avoiding use of the object-oriented
features of C++ or they don’t use C++ at all and are waiting for others to establish the path to certification when using
C++.

C++ has many features that make it a good programming language selection from both business and engineering
perspectives. Some of these features include an encapsulation mechanism (classes), support for re-use and extensibility
(inheritance), broad hardware support based on a large selection of compilers, and instant familiarity with the core
language constructs by new developers with a C background.

While C++ has attractive features, some of these features also create areas of concern for systems where safety is
paramount. This paper attempts to expose those areas of concern and provide guidance on how to minimize or eliminate
these issues from the software design and implementation.

1.1 Scope
The purpose of this document is to provide a background of the safety concerns associated with the use of some features
of the C++ programming language and to provide guidance for dealing with these issues. It does not attempt to map
these issues to guidelines, objectives, rules, or standards of any specific group or organization, such as DO-178B or the
FAA.

The intent is to raise the awareness of the issues among software developers so they can make measured, informed
decisions when designing and building software. It does not prescribe solutions. Instead it explains the issue and
presents strategies that can be used to mitigate the issue.

1.2 Referenced Documents


If not specified, the latest revision of the indicated documents applies.

1.2.1 Technical
Document Name Number
1 Effective C++ Second Addition, 50 Specific Ways to Improve Your Programs and Designs, Scott
ISBN 0-201-92488-9
Meyers
2 More Effective C++, 35 New Ways to Improve Your Programs and Designs, Scott Meyers ISBN 0-201-63371-X
3 Large Scale C++ Software Design, John Lakos ISBN 0-201-63362-0
4 The C++ Programming Language – Third Edition, Bjarne Stroustrup ISBN 0-201-88954-4
5 Design Patterns, Elements of Reusable Object-Oriented Software, Erich Gamma, Richard Helm,
ISBN 0-201-63361-2
Ralph Johnson, John Vlissides
6 International Standard, Programming Languages – C++ ISO/IEC 14882:1998

1.2.2 Industry
Document Name Number
RTCA/DO-178B
Software Considerations in Airborne Systems and Equipment Certification
EUROCAE/ED-12B

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 5 of 23
2 C++ FEATURES WITH ISSUES
The idea for what is now known as C++ first came onto the programming scene in 1984 and was then called C84. It was
based on C with Classes, which first appeared in 1979. The seminal book, The C++ Programming Language, by Bjarne
Stroustrup [4], was first published in 1985. This book was the starting point for the C++ standards committee. Over the
years, C++ evolved into what was eventually ratified by ISO/IEC as the C++ standard in 1998 [6].

The following list identifies features of the C++ programming language that have been called into question in regards to
raising potential issues. These issues concern both functional and object-oriented features of the language. Many of
these issues are not unique to C++, but are included here for completeness. The potential issues raised are organized in
this document based on features of the C++ language.

 Function Overloading
 Structural Coverage
 Function Selection
 Classes
 Compiler Generated Code
 Constructor/Destructor Invocation
 Potential Compiler Generated Functions
 Default Constructor
 Copy Constructor
 Destructor
 Assignment operator
 Global and Static Object Construction/Destruction Functions
 Dynamic Dispatch for Virtual Functions
 Inheritance (Single and Multiple)
 Possible Dead/Deactivated Code
 Possible errors due to methods and attributes inherited via multiple paths (Diamond of Death)
 Data and Control Coupling (and Flow)
 Polymorphism (Virtual Functions)
 Dynamic Dispatch Structural Coverage
 Function Selection
 Possible Dead/Deactivated Code (due to function overrides)
 Constructor/Destructor Virtual Function Usage
 Data and Control Coupling (and Flow)
 User Defined Implicit Type Conversions
 Loss of Data
 Function Selection
 Data and Control Coupling (and Flow)
 Encapsulation
 Data and Control Coupling (and Flow)
 Object Lifetime
 Global Object Construction Order
 Inline Functions
 Possible Dead/Deactivated Code (branches)
 Templates
 Structural Coverage
 Parameter Selection
 Possible Dead/Deactivated Code

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 6 of 23
Each of these potential issues of the various C++ language features will be discussed in the following sections. Each
issue will be explained and potential strategies for mitigating these issues will be identified.

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 7 of 23
3 CONSIDERATION OF C++ FEATURES WITH ISSUES

3.1 Function Overloading


Function overloading is a mechanism by which two or more functions with the same name may co-exist. Examples of
overloading that commonly exist are mathematical operators such as addition (+), subtraction (-), division (/), and
multiplication (*). The ability to overload user defined functions allows for the easy and natural extension of the
language.

In C, function overloading of user defined functions is not allowed. This leads programmers to rely on conventions for
creating families of related functions where, typically, the data type is included in the function name (e.g. sort_ints and
sort_doubles).

C++ allows function overloading, permitting users to use a single name (e.g. sort) to be used for multiple data types. The
rules for overloading in C++ are very specific. A function can be overloaded only if the signature of each method is
unique. The signature of a method includes the name, number and type of the parameters, and, for class member
functions, the const-ness (constant vs. non-constant) of the method. The return type of a method is not part of the
method signature nor is the const-ness of the parameters.

3.1.1 ISSUE: Structural Coverage


Function overloading has the potential to obscure structural coverage of source code. Because two or more functions
exist with the same name, it is possible that achieving structural coverage of one of these methods may mask the lack of
structural coverage in another if the structural coverage tools and methodology cannot uniquely identify the functions.

MITIGATION:

As long as the chosen methodology for demonstrating structural coverage can unambiguously identify each function (by
means such as the function signature), this potential issue is not an issue. The qualification process for demonstrating the
suitability of the chosen structural coverage process should verify that the process is able to unambiguously identify
overloaded functions.

3.1.2 ISSUE: Function Selection


A side effect of function overloading is the possible ambiguity that may arise when coding; which overloaded function is
actually being called? This ambiguity can lead to programmer confusion.

MITIGATION:

The benefit of using natural, meaningful names for related functions far exceeds the potential downside. Also,
overloading of some methods may be required if the class is to be used with templates.

As an example of the benefits of function overloading, consider two scenarios:

 A data class, X, where two objects of type X can be added. It is much more natural to write “a + b” or even “add(a,
b)” than to write “add_X(a, b)”.
 A function to sort an array of data. As long as the data type to be sorted supports a comparison operator and an
assignment operator, a template function can be written once and used for all data types.

Based on the benefits of function overloading, use of this feature of C++ is highly desirable. Software that uses function
overloading should use a combination of coding standards and reviews to catch and eliminate the potential issues with
function overloading. Adherence to the following guidelines will mitigate this issue.

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 8 of 23
 Overloaded operators must perform the action normally associated with the operator name (i.e. + must add, not
subtract).
 All overloaded methods must perform the same logical action (i.e. a family of methods named sort must all sort
data, one cannot randomize data).
 When using function overloading, the need for an overloaded method must be considered. Overloading simply
because you can is not sufficient (i.e. having two methods named x, one that reads an attribute x and one that writes
the same attribute fails the “need” criteria and would not be allowed).

These guidelines can be turned into coding standards or review checklist items as needed.

Another available means to confirm that the correct overloaded function is selected is testing. If the code that calls the
overloaded function is tested (unit, integration, or system level) without stubbing the overloaded functions, structural
coverage is achieved on the code that calls the overloaded function, and the tested code works as designed, it can be
concluded that the correct overloaded function was selected.

3.2 Classes
Classes are the means by which C++ implements the object-oriented concepts of inheritance, polymorphism, and
encapsulation. Support for these three concepts form the fundamental difference between a functional language like C,
and an object-oriented language like C++. The infrastructure and complexity of the source code as well as the object
code generated by the compiler to utilize and support these concepts can raise issues about many aspects of the software.
These issues are enumerated in the following sections.

3.2.1 Compiler Generated Code

3.2.1.1 ISSUE: Compiler Generated Constructor/Destructor Invocation


The C++ language standard requires that the compiler generate code to automatically call the constructor and destructor
of locally declared objects. The standard requires that the constructor of an object be called when the object comes into
scope. Conversely, the standard requires the destructor of the object be called when the object goes out of scope. Many
programming patterns rely upon this feature of the language to increase safety and reliability. An example of a pattern
that relies on the constructor and destructor being called is the Resource Acquisition is Initialization pattern [4, pg. 366].

The Resource Acquisition is Initialization pattern utilizes a class to provide access to a resource. In this pattern, a
temporary object that encapsulates the resource is created. The constructor of the object acquires the resource, typically
enforcing exclusive access. The destructor releases the resource, typically allowing others to access it once again. The
fact that the destructor is automatically called guarantees that the resource is released. In a functional language, this type
of access is usually accomplished using reciprocal function calls; the first call acquires the resource and the second
releases it. This type of interface is error prone because programmers may forget to release the resource. Other issues
with the functional version occur when more than one resource requires access. It is generally an error to release a set of
resources in a different order than they were acquired (think about deadlock and mutexes). The C++ language requires
that objects be destructed in the opposite order that they were constructed. Using the Resource Acquisition is
Initialization pattern ensures that the resources are released in the reverse order as they were acquired.

MITIGATION:

The C++ language standard requires the compiler to generate calls to the constructor and destructor for all local variables
that are classes (structures are types of classes). Because the standard calls for this compiler generated code and because
the code must contain the language keyword class (or struct) to cause the compiler to generate the constructor and
destructor calls, this compiler generated code is directly traceable to the source code. This is demonstrated by the
following fact: Removal of the keyword class (or struct) from the source code inhibits the compiler from generating the
code.

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 9 of 23
Based on the fact that the constructor and destructor calls are directly traceable to the source code, this is not an issue
from the perspective of whether compiler generated code is traceable to the source code. There remains the question of
whether the code generated by the compiler for these constructor and destructor calls is correct. Proof of the correctness
of this code is normally verified by testing scenarios that test the constructor and destructor of the class. If the
constructors and destructors work, it can be inferred that they were called correctly.

3.2.1.2 ISSUE: Potential Compiler Generated Functions


The C++ language standard requires that the compiler generate several functions for a class under certain circumstances
if the programmer does not provide them explicitly. These functions are:

 Default constructor
 Copy constructor
 Destructor
 Assignment operator

The presence of each of these functions in the object code is not directly traceable to the source code. It is the absence of
source code that causes the compiler to automatically generate these methods. The language standard does have other
requirements that prevent the compiler from automatically generating some of these methods, but again, it is the absence
of specific source code that causes the compiler to generate the functions.

MITIGATION:

There are two possible approaches to resolving this issue. One is to go ahead and let the compiler generate these
methods behind the scenes and then establish some means to prove they are correct (simple matter of testing them) and
to prove that structural coverage is achieved (not possible from a source code perspective).

A second strategy for resolving this issue is to prevent the compiler from automatically generating these functions in the
first place. This can be accomplished, at least partially, by two techniques.

The first technique for preventing the compiler from automatically generating some functions required by the language
standard is to define the class in a manner that, per the C++ standard, prohibits the compiler from generating the
functions. The compiler can be prevented from creating the default constructor by explicitly declaring at least one non-
copy constructor. The compiler can be prevented from creating an assignment operator by declaring a class attribute as
reference or a constant. These two strategies prevent the compiler from generating two functions, but they do not
address the fact that the compiler generates a copy constructor or a destructor.

The second technique for preventing the compiler from generating any of these functions is to explicitly declare the
methods in the source code. Because the functions are explicitly declared, the compiler will not automatically generate
them (per the standard). This technique eliminates all code the compiler might generate for these functions. If the
methods are not desired because the design does not call for them, they can be declared as private methods and then not
defined. Declaring them private will cause a compile error any time someone attempts to use them. By not defining
them (i.e. not implementing them), link errors will be generated if the class itself (or a friend) tries to use them. By not
defining the methods that are not desired, the issue of dead and deactivated code is avoided.

3.2.1.3 ISSUE: Global Object and Static Object Construction/Destruction Functions


The C++ language standard requires that all global and static objects be initialized (constructed) before any function in
the same translation unit (typically a cpp file) where the objects are defined is executed. The standard also requires that
these same objects be destroyed (destructor called) when the program ends. This requirement forces the compiler to add
code to call the constructors and destructors for global and static objects that are defined in each translation unit.

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 10 of 23
MITIGATION:

The code to construct and destruct global and static objects that is generated by the compiler is traceable to the source
code. For static objects, it is directly traceable to the static keyword. For global objects, it is traceable to the scope at
which an object is declared. Removal of the static keyword and relocating variables from global scope to some lesser
scope will eliminate the code automatically generated by the compiler (a different set of code is generated to call the
constructor and destructor – see 3.2.1.1).

Based on the fact that the functions to invoke the constructor and destructor of all global and static objects is directly
traceable to the source code, this is not an issue from the perspective of whether compiler generated code is traceable to
the source code. There remains the question of whether the code generated by the compiler is correct. Proof of the
correctness of this code is normally verified by testing functions and logic that use these global and static objects.

Although this issue is easily addressed, another more critical issue with the use of global and static objects remains (see
3.2.6.1).

3.2.1.4 ISSUE: Compiler Generated Dynamic Dispatch for Virtual Functions


In C, whenever a function call appears in the source code, the compiler determines, at compile time, which method is to
be called simply by examining the name of the function. Based on the name, the compiler effects the call via an
assembly language jump, branch, or some similar instruction. This process is referred to as static dispatch. It is called
static because all the logic to determine which method to invoke is performed at compile time by static analysis of the
source code.

In C++, for non-virtual functions (explained later), function calls work in much the same manner as in C. The difference
is that function overloading dictates that more than just the name of the function be considered when determining which
function should be called (see 3.1).

Object-Oriented Programming defines the concept of polymorphism. Loosely, polymorphism means “many forms”. In
an object-oriented discussion, it refers to the ability of a family of related objects to perform different actions based on
the receipt of the same message. In C++, messages take the form of function calls. Therefore, polymorphism in C++
refers to the ability of objects within a class hierarchy to perform different actions when the same function is invoked.

The ubiquitous example of polymorphism is a class hierarchy that has an abstract shape class with a draw method (see
Figure 1). Two derived classes, circle and square, inherit from the shape class and implement the draw method. A
graphical application that has a collection of shapes can iterate over each of these shapes, sending it a draw message.
Each shape is drawn as either a circle or a square based on the specific (or run-time) type of the object, a circle or a
square.
Shape

<<abstract>> draw()

Circle Square

draw() draw()

Figure 1 Virtual Function Example


Dynamic dispatch is the mechanism by which C++ implements the object-oriented concept of polymorphism. In the
shape example, the particular draw method to be invoked, the one for a circle or the one for a square, is determined at

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 11 of 23
run-time. When the draw message is to be sent to a shape object, the object type is determined, and the draw method for
that particular type is invoked.

In C++, the keyword virtual is used to indicate to the compiler that calls to a method should be dynamically dispatched.
Most C++ compilers implement dynamic dispatch using lookup tables commonly referred to as v-tables, short for virtual
function tables. For each class in a class hierarchy, the compiler generates a table of pointers to functions, one pointer
for each virtual method of a class. Each instance of a class contains a hidden attribute that is a pointer to the v-table for
the run-time type of the class. At run-time, when a method is invoked, the v-table pointer is used to find the class
specific v-table. The class specific v-table is then indexed to invoke the method. Thus the method invoked corresponds
to the run-time type of the object.

As previously explained, compilers generate extra code to perform dynamic dispatch. In a typical compiler, this code
performs double indirection to determine the appropriate function to call (i.e. pointer-to-pointer dereferences). Based on
the fact that the code to perform dynamic dispatch is directly traceable to the source code, this is not an issue from the
perspective of whether compiler generated code is traceable to the source code (i.e. the virtual keyword). The questions
remain as to whether the code generated by the compiler is correct and whether structural coverage of that code is
achieved.

MITIGATION:

In order to address the issue of correctness and structural coverage of compiler generated code for dynamic dispatch, two
tactics can be utilized. First and foremost, the code generated by the compiler should be analyzed to verify the code is
safe, reliable, correct, and generally acceptable based on the coding standards and guidelines of the project. An
important factor in this analysis is to verify that the dynamic dispatch code does not contain any condition/decision logic.
The lack of condition/decision logic ensures that, if the code is executed, it achieves structural coverage. Second, this
code should be exercised during testing. Two methods for executing this code are suggested.

The first method for testing the compiler generated code for dynamic dispatch involves the actual design of the source
code. It requires a design as follows:

When a virtual method is desired, a non-virtual method is used in its place. A corresponding virtual method is declared
as a private (or possibly protected) method of the class. A name similar to the public non-virtual method name is
recommended so the two are tied together and the relationship is obvious (an example would to use the same name for
both, except add a prefix of “v_” to the virtual method name). The non-virtual method then invokes the virtual method
as its only action. An example is illustrated in Figure 2.

IO_Protocol

decode() Pseudo-Code:
encode()
<<abstract>> v_decode() IO_Protocol::decode()
<<abstract>> v_encode() {
v_decode();
}

IO_Protocol::encode()
IO_GPS_Protocol {
v_encode();
<<virtual>> v_decode() }
<<virtual>> v_encode()

Figure 2 Virtual Function Interface Strategy

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 12 of 23
This strategy has two side effects. First, it isolates the decision for the interface of the class from the decision to make
the interface virtual. Good design calls for separation of design decisions (i.e. decoupling). This accomplishes that
objective.

The second side effect is that the compiler is forced to use dynamic dispatch to invoke the virtual method. If the virtual
method were called directly using a named object, the compiler might be able to determine the run-time type of the
object at compile time and thus bypass the dynamic dispatch logic. Compilers can often make this static determination
when virtual methods are invoked using objects that are declared locally (or globally). By invoking the method on an
unnamed object (i.e. the implicit object defined by the this pointer within a class method), the compiler has no way of
knowing the run-time type of the object at compile time and thus must use dynamic dispatch.

This first strategy works for all virtual methods except virtual destructors. It is not possible to make the destructor non-
virtual and have it call a virtual function to destroy the object (see 3.2.3.4). Ensuring that dynamic dispatch works
properly for a virtual destructor under this strategy is complicated. It can be argued that, by comparison, if dynamic
dispatch works for the other virtual methods and the object is destructed correctly, then the code generated by the
compiler is correct.

The second method for testing the compiler generated code for dynamic dispatch is very similar to the first, but affects
the tests and does not require any design constraints within the production code. Under this method, forcing dynamic
dispatch is accomplished within the test code by using dispatching test functions.

A dispatching test function is nothing more than a simple function that invokes a virtual method on a user supplied
object. The function must be passed either a pointer or a reference to an object within the class hierarchy that is being
tested. The parameter is then used to call the virtual method.

The concept is the same as before: Because the compiler cannot know at compile time what the runtime type of the
actual object passed to the function will be, it must use dynamic dispatch to call the correct function. The actual type
used as the function parameter is not constrained to be a base class within the class hierarchy. Any class within the class
hierarchy will suffice because the compiler does know that the class being passed into the test function is the final class
in the hierarchy and thus cannot make any assumptions about the finality of the virtual method being invoked. Based on
this, it must use dynamic dispatch regardless of where in the hierarchy the actual parameter type lies.

This second strategy also suffers from a weakness in terms of virtual destructors. If a test function is created to call a
virtual destructor, it raises the possibility that the destructor will be called twice; once by the test function and once by
the owner of the object. This is not advisable. Again, it can be argued that, by comparison, if dynamic dispatch works
for the other virtual methods and the object is destructed correctly, then the code generated by the compiler is correct.

3.2.2 Inheritance (Single and Multiple)


Object-oriented programming defines the concept of inheritance. Inheritance is a mechanism that allows a class to
derive its state and behavior from another class. When a class derives or inherits from another class, it takes on the
fundamental characteristics of the class from which it is derived (referred to as the parent, base, or super-class). In C++,
those fundamental characteristics include the interface (a.k.a. methods) and the attributes (a.k.a. member data) of the
parent class.

One of the key tenets of object-oriented programming is overriding. A derived class is said to override its parent if it
performs an action that is different from its parent given the same stimulus. In object-oriented circles, this stimulus is
referred to as sending the object a message. In C++, sending an object a message is accomplished by invoking a method
of the object. In C++, the parent (or its parent) must explicitly state that it allows derived classes (referred to as children
or sub-classes) to override a method. This is accomplished using the keyword virtual.

Inheritance is a feature of object-oriented programming that facilitates code reuse. Software designed in object-oriented
systems with the specific goal of reuse typically uses inheritance. Reuse can be accomplished in two ways: 1) direct
reuse by inheriting the methods and attributes of the parent class, or 2) indirect reuse by inheriting the interface of the

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 13 of 23
parent class, but not the implementation. Inheritance allows software to use derived classes without having to formally
know those derived classes. See Figure 3 for an example of both forms.

IO_Protocol
CRC_Utility
computed_CRC decode()
encode()
compute_CRC() <<abstract>> v_decode()
<<abstract>> v_encode()

IO_GPS_Protocol
Binary_validator

<<virtual>> v_decode()
is_valid()
<<virtual>> v_encode()

Figure 3 Reuse via Inheritance

3.2.2.1 ISSUE: Possible Dead/Deactivated Code


An issue raised in connection with inheritance has to do with dead and deactivated code. When a child overrides a
method of a parent class, it might result in dead or deactivated code in the parent. The term might is used, because the
code could still be used either via another inheritance hierarchy or it may be called directly by the overriding method. If
neither of these scenarios exist, the code is, at best, deactivated, and at worst, dead.

MITIGATION:

This issue is closely related to dynamic dispatch (3.2.1.4). The issue with dynamic dispatch is whether virtual methods
are called. The same strategy to prove that dynamic dispatch is working (structural coverage) can be applied here to
prove that a function override does not create dead-code. Traditional structural coverage methods can be used as long as
those methods can uniquely identify each virtual method within a class hierarchy.

The verification of structural coverage of all methods within a class hierarchy demonstrates the lack of dead code. The
detection of deactivated code is not as simple. This process relies on manual verification. This verification should use a
combination of coding standards and reviews to catch and document deactivated code. It is not recommended that code
that has been deactivated be removed because this has a significant potential to inject errors into the remaining design
and code.

3.2.2.2 ISSUE: Possible errors due to methods and attributes inherited via multiple paths
Multiple inheritance is the ability of a class to directly inherit from more than one base or parent class. In the object-
oriented community, C++ is in the minority of object-oriented languages in that it provides direct support for multiple
inheritance. The issues that arise with the use of multiple inheritance include the issues present with all forms of
inheritance (3.2.2) as well as an additional issue related to inheritance of the same method or attribute from different
parents. Figure 4 illustrates two forms of this issue.

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 14 of 23
Vehicle

Gunfighter Graphic <<abstract>> turn()

<<virtual>> draw() <<virtual>> draw()

Airplane Boat

<<virtual>> turn() <<virtual>> turn()

Gunfighter Graphic

Seaplane

Figure 4 Redefinition via Multiple Paths

MITIGATION:

The left half of Figure 4 demonstrates the first form of redefinition. In this form, two orthogonal concepts, a gunfighter
and a graphic symbol, are combined to form a new class. The problem is that the two orthogonal concepts share a
common vocabulary, but the common vocabulary has different meanings. The new class is confused as to what it means
to draw; should it retrieve its gun from the holster or should it render itself to a display? A solution to this issue is to
refactor the design to eliminate the overlapping vocabulary. In this example, the draw method in the Graphic class could
be renamed to render to resolve the overlap.

The right half of Figure 4 demonstrates the second form of redefinition. In this form, two similar concepts are combined
to form a new, aggregate class. In this case, the shared vocabulary of turn has the same conceptual meaning, but how is
turning accomplished in this new class; does one use a steering wheel or a joystick? In general, this form of multiple
inheritance should be avoided as it is typically a sign of a questionable design. If, after careful consideration, it is
decided to use this form of multiple inheritance, there is at least one possible design solution to mitigate this issue.

One design solution in this example is to override the turn method in the seaplane class. The language requires this if it
is desired to substitute a seaplane object wherever a vehicle object is requested. The language states that a call to turn is
ambiguous in this scenario. To resolve this ambiguity, one must override the turn method in the seaplane class and
define how to turn a seaplane. The fact that the language forces this design to override the turn method prevents any
ambiguity that a programmer may have.

The C++ language has similar requirements for addressing attributes that are inherited through multiple paths.
Programmers must explicitly call out the inheritance path of any common base class attribute (attributes of the vehicle
class in this case) when referencing a base class attribute.

In the majority of cases of multiple inheritance that take this second form, the intention is to have only one copy of all
the common base class attributes in the resulting class (i.e. only want one set of vehicle attributes in a seaplane). In C++,
the default behavior is for the resulting object to contain one set of base class attributes per inheritance path. In the
example, each seaplane object would have two copies of all vehicle attributes.

The default behavior of the language should be overridden by using the virtual keyword when specifying multiple
inheritance. This ensures that only one copy of the common base class attributes will be present in the new class.
______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 15 of 23
Before using this solution, a careful study of the design should be performed to make sure the design is valid. Typically
this form of multiple inheritance can be avoided by refactoring to eliminate the use of multiple inheritance and thus
eliminate the ambiguity of the common methods. By refactoring to use single inheritance, only one of each of the base
class methods and attributes will be present, thus avoiding all potential ambiguity.

3.2.2.3 ISSUE: Data and Control Coupling (and Flow)


An issue raised in connection with inheritance has to do with data and control coupling (as well as data and control
flow). The problem is that the overriding feature that comes along with inheritance has the potential to affect the essence
of what is being tested. As an example of what this means, consider the class hierarchy illustrated in Figure 5.

Base Pseudo Code for Base::run


attribute_1 {
method_A();
run() method_B();
<<virtual>> method_A() }
<<virtual>> method_B()

Derived_1 Derived_2
attribute_2 attribute_3

<<virtual>> method_B() <<virtual>> method_A()


<<virtual>> method_C() <<virtual>> method_D()

Figure 5 Data and Control Coupling Example

Figure 5 depicts two classes, Derived_1 and Derived_2, that both inherit from a single class, Base. Now consider the
method run that is present in the class Base. The question raised here is what degree of coupling is present between the
class Base and its derived classes, Derived_1 and Derived_2. The simple answer is none. The class Base is completely
independent of the two derived classes.

It is true that the class Base is autonomous from the two derived classes. That is, if the two derived classes were
removed from the system, all instances of the class Base will continue to function as intended. Based on this apparent
independence, one might ask: “If structural coverage of all the methods in class Base is achieved, is that sufficient
regardless of the presence of derived classes”? The answer to this is a resounding maybe.

The reason the answer to the question is maybe is because of the presence of virtual methods. These virtual methods
introduce the possibility of overriding. Consider what happens when the method run is invoked on objects of the various
types. Table 1 shows the call sequence that occurs when the run method is invoked on objects of each type.
Table 1 Call Sequence for Base::run method
Object Type Base Derived_1 Derived_2
Call Sequence for the Base::method_A() Base::method_A() Derived_2::method_A()
run() Method Base::method_B() Derived_1::method_B() Base::method_B()

From Table 1, two things are apparent. The first is that the run method might seem to be a simple function, but, in the
presence of overriding, it exhibits a more complex behavior. The second apparent issue is that, if overriding is present,
simple testing and structural coverage of each method in a class may not be sufficient.
______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 16 of 23
It should be noted that, although the above example deals with method invocation and thus is in the realm of control
coupling and flow, the same issue is relevant for data coupling and flow. To illustrate the data coupling and flow issue,
consider attribute_1 in the class Base. What if, for example, Base::method_A is the only method that uses attribute_1? If
this is the case, attribute_1 is used by instances of the Base and Derived_1 classes, but not by instances of the Derived_2
class. Thus, the presence of overriding has impacted the data coupling and flow with respect to attribute_1 in the context
of the run method.

MITIGATION:

In order to address the issue raised regarding data and control coupling (as well as data and control flow) in the presence
of overriding, it is recommended that the “flattened” form of each class be tested. The “flattened” form of a class,
simply put, is what the class would look like if it retained the same interface and implementation but was written without
the benefit of inheritance. The flattened form of the classes from Figure 5 is shown in Figure 6. In Figure 6, methods of
the flattened classes are prefixed with their originating class names for clarity.

Base
attribute_1

run()
method_A()
method_B()

Flattened_Derived_1 Flattened_Derived_2
Base::attribute_1 Base::attribute_1
Derived_1::attribute_2 Derived_2::attribute_3

Base::run() Base::run()
Base::method_A() Derived_1::method_A()
Derived_1::method_B() Base::method_B()
Derived_1::method_C() Derived_2::method_D()

Figure 6 Data and Control Coupling - Flattened

If the chosen methodology for demonstrating structural coverage can identify the flattened form of a class, testing in
conjunction with structural coverage should be sufficient to mitigate these issues. If the chosen structural coverage
methodology does not understand the flattened form of a class, the methodology must be enhanced to include an analysis
of the class hierarchy with respect to overriding. It may be helpful to test each class within a hierarchy independently in
order to determine the amount of coverage across the entire hierarchy that can be attributed to each specific class. This
information can then become the basis of an analysis that ensures the flattened form of the class is tested and structurally
covered.

It should be noted that the issues of data and control coupling and flow due to overriding are not always present. An
analysis of the class hierarchy may be used to justify the extent to which each class within the hierarchy must be
independently tested. This analysis must be performed for each class within the hierarchy.

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 17 of 23
3.2.3 Polymorphism (Virtual Functions)

3.2.3.1 ISSUE: Dynamic Dispatch Structural Coverage


This is the same issue as that identified in section 3.2.1.4. It is duplicated here to indicate that the issue has more than
one conceptual basis. See section 3.2.1.4 for the issue and the mitigation strategy.

3.2.3.2 ISSUE: Function Selection


Polymorphism in C++ is a form of function overriding (see 3.2.2). Classes that inherit from another may override
methods of the inherited class. This overriding of methods might be a source of confusion for programmers new to the
concept of polymorphism.

MITIGATION:

Because this potential issue occurs due to the unfamiliarity of programmers with the concept of polymorphism, it is
recommended that education and supervision be the primary means to mitigate this issue. As an adjunct, it is also
recommended that the review process require that at least one experienced C++ developer attend each review.

3.2.3.3 ISSUE: Possible Dead/Deactivated Code (due to function overrides)


This is the same issue as that identified in section 3.2.2.1. It is duplicated here to indicate that the issue has more than
one conceptual basis. See section 3.2.2.1 for the issue and the mitigation strategy.

3.2.3.4 ISSUE: Constructor/Destructor Virtual Function Usage


When objects that involve inheritance are constructed and destructed in C++, the standard requires the constructor and
destructor of all the classes in the inheritance hierarchy be called in a specific order. For constructors, the standard
requires the constructors be called such that it follows the inheritance hierarchy. For example, in the shape example in
Figure 1, the shape class is the base class and thus its constructor is called first. After the shape part of the object is
constructed, the circle (or square) portion is constructed by calling its constructor. For destruction, the process is
reversed, with the circle (or square) portion destructed first, followed by the shape part. The construction and
destruction process for all objects proceeds in this fashion, regardless of the number of classes in the inheritance
hierarchy and whether single or multiple inheritance is used.

Because objects are constructed in phases based on the inheritance hierarchy, it is meaningful to consider that an object’s
runtime type changes during the construction process. Again, with a circle object in the shape example, when the shape
constructor completes, the object is a shape, but not yet a circle. When the circle constructor completes, the object is
both a shape and a circle. During the destruction process, prior to the circle destructor running, the object is both a
shape and a circle. When the circle destructor completes but prior to the shape destructor running, it is a shape, but not
a circle.

Based on the step-wise runtime type conversion that objects undergo during construction and destruction, it is risky as
best, and an error at worst, to invoke virtual methods during these sequences. Invocation of virtual methods during the
construction and destruction phases of an object’s lifetime is an issue because the specific virtual method that is invoked
depends upon the changing runtime type of the object.

MITIGATION:

In order to prevent errors in the construction and destruction phases of an object’s lifetime, calling virtual functions from
the constructor and destructor should be avoided. Virtual functions should be called neither directly nor indirectly (i.e.
do not call a non-virtual method that in turn calls a virtual method). This guideline can be turned into a coding standard
or review checklist item as needed.

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 18 of 23
3.2.3.5 ISSUE: Data and Control Coupling (and Flow)
This is the same issue as that identified in section 3.2.2.3. It is duplicated here to indicate that the issue has more than
one conceptual basis. See section 3.2.2.3 for the issue and the mitigation strategy.

3.2.4 User Defined Implicit Type Conversions


The C++ programming language provides features that allow programmers too easily extend the language by defining
their own types and operators for those types. An example of this is the complex number class provided in the C++
library. This class allows programmers to declare and use complex numbers in much the same way as they do integers.
Almost everywhere a programmer can use an integer, they can use a complex number as well. For example, they can
add and subtract them using the same syntax as they do for integers (e.g. a = b + c).

The ability to extend a language using the native syntax of the language is a powerful asset. Familiarity reduces the
learning curve and, more importantly, the likelihood of misunderstanding related defects. Furthermore, the ability to
understand complex algorithms is brought within the reach of less experienced developers because of the natural
expression of the algorithm in terms that anyone familiar with the language will recognize.

C++ facilitates some of this language extensibility by allowing implicit type conversions. C programmers are familiar
with implicit type conversions because they are part of the C language as well. Pass a float variable to the majority of C
math library functions and that variable will be implicitly converted to a double because the C math library provides an
interface that works almost exclusively with doubles.

C++ allows users to define implicit conversion between types using class constructors and cast operators. Any class
constructor that requires a single parameter can be used to convert that parameter into an instance of the class. Classes
can also provide casting operators that allow an instance of one class to be implicitly converted to another class or a
built-in type (int, float, etc.).

3.2.4.1 ISSUE: Loss of Data


The issue that arises from implicit type conversions is readily understood by anyone that is familiar with C. Some
conversions can cause a loss of data. For instance, a conversion from long integer to a short integer can cause a loss of
data if the value of the long integer exceeds the maximum (or minimum) value that a short integer can represent. The
same problem can occur with user defined types in C++.

MITIGATION:

In order to prevent loss of data due to user defined type conversions, implicit conversions that can lose data should be
disabled. This is accomplished in C++ using the explicit keyword. Any constructor that takes a single parameter or
more than one parameter but with default values for all parameters after the first can be used for implicit conversion.
Defining these constructors to be explicit disables the use of these methods for implicit conversion. The explicit
keyword also works for cast operators.

In general, implicit conversions should be disabled by default, with only special cases allowed to provide implicit
conversion. These special cases should be well understood and analyzed to ensure that they will not introduce the
possibility of data loss. Each implicit conversion that is allowed should be tested.

3.2.4.2 ISSUE: Function Selection


Another issue triggered by implicit type conversions is the potential ambiguity relating to determining which function is
actually being invoked to generate the conversion. This issue is the same issue that is present with function overloading
(3.1.2). If more than one function exists by the same name, which one of those methods is being invoked given the
parameters that are being passed to it.

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 19 of 23
MITIGATION:

The mitigation of this issue is the same as for function overloading. See that issue for the mitigation strategy (3.1.2).

3.2.4.3 ISSUE: Data and Control Coupling (and Flow)


Another issue triggered by implicit type conversions is the impact this has on data and control coupling (as well as data
and control flow). Because user defined implicit type conversion occurs though a function call (class constructor or
conversion operator), the coupling and flow of the program is affected whenever one of these functions is invoked
implicitly by the compiler.

MITIGATION:

In C and C++, implicit type conversions are static in nature. That is, the conversion to be performed can be statically
determined by examining the source code. Therefore, this issue reduces down to an understanding of the language and
the context in which the language is applied. If one understands the rules of the language and the design of the software,
the data and control coupling (as well as data and control flow) issues related to implicit type conversion could be
determined in the same manner as they are for regular function calls.

Due to the fact that this issue has two causes, both being knowledge related, it is recommended that education and
supervision be the primary means to mitigate this issue. As an adjunct, it is also recommended that the review process
require that at least one experienced developer attend each review. The relevant experience of the reviewer in this case
refers to experience with both the C++ language and with the design and implementation of the software (both the
software being reviewed as well as the software it uses).

3.2.5 Encapsulation

3.2.5.1 ISSUE: Data and Control Coupling (and Flow)


An issue raised regarding encapsulation has to do with data and control coupling (as well as data and control flow). The
issue is not that encapsulation increases coupling (or flow), but rather that it obscures it. Consider a class that has a
private attribute. If it exposes that attribute by a method such as get_attribute, users of this class that invoke the
get_attribute method are coupled to the attribute. This coupling is not readily apparent simply by examining the site of
the function call.

MITIGATION:

This issue is yet another issue that hinges upon the knowledge possessed by the developer. This knowledge involves two
components. The first component is knowledge of the language and the second is knowledge of the classes involved
(both the class of the method making the call and the class being called).

Due to the fact that this issue has two causes, both being knowledge related, it is recommended that education and
supervision be the primary means to mitigate this issue. As an adjunct, it is also recommended that the review process
require that at least one experienced developer attend each review. The relevant experience of the reviewer in this case
refers to experience with both the C++ language and with the design and implementation of the software (both the
software being reviewed as well as the software it uses).

3.2.6 Object Lifetime

3.2.6.1 ISSUE: Global Object Construction Order


The C++ language states global and file scope objects that are defined within a translation unit will be initialized in the
order they are defined within the unit. It also states that the order of initialization of global and file scope objects across
translation units is undefined.

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 20 of 23
This means that if one file defines two global variables, x and y, in that order, the initial value of y can safely depend
upon the initial value of x (same translation unit), but not vice-versa. Also, if there are two files, one defining the
variable p and the other defining the variable q, neither p nor q can safely depend upon the initial value of the other
because the initialization order (across translation units) is undefined.

The lack of a defined construction order for global and file scope variables can cause issues with the correctness of the
initial state of a system.

MITIGATION:

The lack of a standard construction order for global and file scope variables leads to one of three possible solutions. The
first possible solution is to avoid all dependencies across translation units. This means that the initial value of all global
and file scope variables in a translation unit must not depend upon any global variables or file scope variables from any
another translation unit. This includes inline functions that reference static class attributes because the inline function is
potentially incorporated into many translation units while the static attribute must only be defined in one translation unit.

A second possible solution is to depend upon compiler specific behavior to ensure the initialization order of these
variables. At least one compiler initializes translation units in alphabetical order. Thus, with this compiler, one could
allow a translation unit to depend on global and file scope variables from other translation units, as long as those
translation units precede it alphabetically. Using this approach minimizes any chance of portability to a different
compiler.

A third option, the one most widely publicized, is to not use global and file scope variables. By creating a function that
returns a reference to a variable, the proper precautions can be taken within that function to ensure that the variable is
initialized prior to its use. This tactic is well known and is referred to as the Singleton Pattern [5].

3.3 Inline Functions


The C++ programming language provides support for inline functions. Inline functions are conceptually the same as
preprocessor macros in C and C++, but they are much better. Anyone who has written C code for any length of time is
familiar with the side effect that can wreak havoc on C macros.

From a programmer’s perspective, C++ inline functions have two facets. First, they look and feel just like a regular
function, so the potential for side effects like those with C macros is eliminated. Second, since they are inline, they have
the potential to generate the same, efficient code that C macros do (i.e. no function call overhead).

The benefits of inline functions over macros include the previously stated lack of side effects. Another benefit is,
because inline functions are true functions, the compiler enforces the type constraints based on the function declaration.
This increases type safety. Based on the benefits of inline functions over macros, it is desirable to use them.

3.3.1 ISSUE: Possible Dead/Deactivated Code (branches)


An issue that arises from the use of inline functions is the potential for dead and deactivated code. Consider an inline
function that contains a condition or a decision. Because the method is inlined, the compiler places the code from the
method directly into the logic that called the inline function. Within any specific context, it might be that the condition
or decision in the inlined function always generates the same result (i.e. is always true or always false). If this is the
case, the branch not taken is deactivated code at best, dead code at worst.

MITIGATION:

The inline keyword in C++ is a recommendation to the compiler. The compiler is not required to actually inline the
method that is adorned with the inline keyword. Many compilers do not inline methods that contain loops (e.g. for, do,
and while loops). Other compilers do not inline methods if they exceed some predetermined size or complexity. Most
compilers will inline methods with simple if statements.

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 21 of 23
The majority of tools currently used to measure structural coverage do not support structural coverage analysis of each
instance where a function is inlined. This is because most structural coverage tools work at the source code level and the
compiler determines the outcome of function inlining. Because of this current lack of tool support, it becomes difficult
to determine structural coverage of inline functions that contain conditions and decisions.

Based on the lack of tool support (both compilers and structural coverage tools), one strategy to mitigate the structural
coverage issue is to not allow functions that contain conditions or decisions to be inlined. It is recommended that the
following or some similar restriction be added as a coding standard or guideline:

 Do not inline functions that contain conditions or decisions.

3.4 Templates
The C++ programming language provides a feature for generic programming called templates. Templates in C++ can be
either function templates or class templates. Generic programming provides a form of reuse where algorithms and
services are written once in a type neutral fashion, and then reused by instantiating them for a specific data type or set of
data types. Ada has a similar, albeit less powerful feature, called generics.

The advantage of generic programming should be immediately obvious to anyone who has cut and paste code to generate
two or more functions with the only difference being the data type (take a sorting algorithm for example). Everything is
fine until you find a bug. At this point, the algorithm is fixed for the data type that demonstrated the error. Hopefully
the development process is rigorous enough to make sure the same fix is applied to every other version that was cut and
pasted.

With templates, the algorithm would be written once and instantiated multiple times. If a defect is found, it is fixed in
the single copy of the source code. All the various instantiations of that code are automatically corrected when the
system is compiled.

3.4.1 ISSUE: Structural Coverage


The use of generic programming, called template programming in C++, can challenge current structural coverage
methodologies. Most current structural coverage tools either do not support templates or do not differentiate between the
same template instantiated with different data types.

MITIGATION:

The verification of structural coverage of all methods of a template and each instantiation of a template for unique data
types should be verified. If the structural coverage methodology can uniquely identify each template function for each
data type for which it is instantiated, structural coverage should find all dead and deactivated code. That code should be
eliminated or documented appropriately.

If the structural coverage methodology can detect coverage of template functions, but not uniquely based on the data
type for which it is instantiated, the following strategy is recommended: Create a separate test for each template
instantiation and run those tests in isolation. If each test demonstrates structural coverage of the template, then the
template functions can be said to be free of dead code for all instantiations of the tested data types.

There still remains the issue of deactivated code. The C++ language standard helps here. The C++ language requires
compilers to only instantiate template code if substantive use of the code occurs. Substantive use occurs when either a
template method is invoked or if the address of a template method is requested. This requirement is on a per-method
basis. Therefore, a template class with two methods, one that is used and one that is not, only the method that is used will
appear in the final object code. Based on this, if the template code is never used, the compiler will not include it in the
final build, thus eliminating the deactivated code.

Compilers can easily be checked for compliance to this part of the standard by creating a template function that contains
code that has syntax errors (either as a standalone function, as a method of a template class, or a template method in a
______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 22 of 23
class) and then compiling that code. If the method is not used substantively and the compiler is compliant, the code
should compile. If the code is used or the compiler is non-compliant, the code will not compile.

With the compiler eliminating all the truly unused template code, deactivated code in templates is reduced to the same
issue as deactivated code in C code and non-template C++ code. This type of deactivated code is found per the normal
methods, those being requirements based testing and analysis. It is typical that a coding standard or guideline be added
to require deactivated code analysis. This coding standard or guideline will also apply to template code.

3.4.2 ISSUE: Parameter Selection


C++ templates use parameters to identify the data types (or integral constants) that the template requires for instantiation.
To developers unfamiliar with C++ templates, it may be confusing as to what the final form of the template will look like
when instantiated. This is especially true for template functions because the compiler deduces the template parameters
based on the parameters supplied when calling the template function.

MITIGATION:

Because this potential issue occurs due to the unfamiliarity of programmers with templates, it is recommended that
education and supervision be the primary means to mitigate this issue. As an adjunct, it is also recommended that the
review process require that at least one experienced developer attend each review.

As a secondary mitigation strategy for this issue, for class templates, it is preferable to declare a typedef that all code will
use when referring to a template instantiation. This ensures that there is one shared name and data type for the template
instantiation in question. This prevents another non-safety issue with templates, namely code bloat. Code bloat is a
phenomenon related to generic programming. Sloppy use of data types can cause the compiler to generate many slightly
different flavors of the generic code. These multiple copies occupy more memory, increasing program size, thus the
name code bloat.

3.4.3 ISSUE: Possible Dead/Deactivated Code


Class templates typically define a class with an interface and implementation. Because template classes form a basis for
reuse, the template class usually contains a complete interface for the abstraction it represents. Users of these template
classes may not require the complete interface of the class. This leads to the potential for dead and deactivated code.

MITIGATION:

The issue of dead and deactivated code in templates is the same issue as that identified in section 3.4.1. It is duplicated
here to indicate that the issue has more than one conceptual basis. See section 3.4.1 for the issue and the mitigation
strategy.

______________________________________________________________________________________________
Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001
Design Considerations for Using C++ in Rev –
Safety Critical Avionics Systems Page 23 of 23

You might also like