c++_safety

Upload: sonali-shree

Post on 08-Apr-2018

217 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/7/2019 C++_Safety

    1/23

    Design Considerations for Using C++ in Safety CriticalAvionics 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

    Safety Critical Avionics Systems

    Rev

    Page 1 of 23

  • 8/7/2019 C++_Safety

    2/23

    Honeywell International Inc. PROPRIETARYThis 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

    Safety Critical Avionics Systems

    Rev

    Page 2 of 23

  • 8/7/2019 C++_Safety

    3/23

    TABLE OF CONTENTS

    1 INTRODUCTION.............................................................................................................................................................5

    1.1 Scope............................................................................................................................................................................5

    1.2 Referenced Documents................................................................................................................................................5

    1.2.1 Technical...............................................................................................................................................................51.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.....................................................................................................................................93.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...........................................103.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..........................................................................................183.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....................................................................................................................................193.2.4.2 ISSUE: Function Selection...........................................................................................................................19

    3.2.4.3 ISSUE: Data and Control Coupling (and Flow)...........................................................................................203.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................................................................................................................................223.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

    Safety Critical Avionics Systems

    Rev

    Page 3 of 23

  • 8/7/2019 C++_Safety

    4/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

    Safety Critical Avionics Systems

    Rev

    Page 4 of 23

  • 8/7/2019 C++_Safety

    5/23

    1 INTRODUCTION

    Every programming language goes through an adoption process where users familiarize themselves with the languageand 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 dont 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 eliminatethese 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

    MeyersISBN 0-201-92488-9

    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,Ralph Johnson, John Vlissides

    ISBN 0-201-63361-2

    6 International Standard, Programming Languages C++ ISO/IEC 14882:1998

    1.2.2 Industry

    Document Name Number

    Software Considerations in Airborne Systems and Equipment CertificationRTCA/DO-178B

    EUROCAE/ED-12B

    ______________________________________________________________________________________________Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001

    Design Considerations for Using C++ in

    Safety Critical Avionics Systems

    Rev

    Page 5 of 23

  • 8/7/2019 C++_Safety

    6/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 wasbased 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

    Safety Critical Avionics Systems

    Rev

    Page 6 of 23

  • 8/7/2019 C++_Safety

    7/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

    Safety Critical Avionics Systems

    Rev

    Page 7 of 23

  • 8/7/2019 C++_Safety

    8/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 ofoverloading 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 thesignature of each method is

    unique. Thesignature 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

    Safety Critical Avionics Systems

    Rev

    Page 8 of 23

  • 8/7/2019 C++_Safety

    9/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 writesthe 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 beconcluded 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 objectcode 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 intoscope. 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 theResource Acquisition is Initialization pattern [4, pg. 366].

    TheResource 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 typeof 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 theResource 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 (orstruct) 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 (orstruct) 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

    Safety Critical Avionics Systems

    Rev

    Page 9 of 23

  • 8/7/2019 C++_Safety

    10/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 asreference 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 inthe 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

    Safety Critical Avionics Systems

    Rev

    Page 10 of 23

  • 8/7/2019 C++_Safety

    11/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 lesserscope 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 asstatic dispatch. It is calledstatic 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 abstractshape class with a draw method (see

    Figure 1). Two derived classes, circle andsquare, inherit from theshape 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

    draw()

    Square

    draw()

    Circle

    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 asquare, is determined at

    ______________________________________________________________________________________________Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001

    Design Considerations for Using C++ in

    Safety Critical Avionics Systems

    Rev

    Page 11 of 23

  • 8/7/2019 C++_Safety

    12/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 virtualis 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 virtualfunction 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 forthe 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 onthe 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 sourcecode. 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 methodas its only action. An example is illustrated in Figure 2.

    IO_Protocol

    decode()

    encode()

    v_decode()

    v_encode()

    IO_GPS_Protocol

    v_decode()

    v_encode()

    Pseudo-Code:

    IO_Protocol::decode()

    {

    v_decode();

    }

    IO_Protocol::encode()

    {v_encode();

    }

    Figure 2 Virtual Function Interface Strategy

    ______________________________________________________________________________________________Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001

    Design Considerations for Using C++ in

    Safety Critical Avionics Systems

    Rev

    Page 12 of 23

  • 8/7/2019 C++_Safety

    13/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 virtualmethod 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 determinationwhen 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 worksproperly 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 beingtested. 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 bythe 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 toderive 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 methodof 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

    Safety Critical Avionics Systems

    Rev

    Page 13 of 23

  • 8/7/2019 C++_Safety

    14/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.

    CRC_Utility

    computed_CRC

    compute_CRC()

    Binary_validator

    is_valid()

    IO_Protocol

    decode()encode()

    v_decode()

    v_encode()

    IO_GPS_Protocol

    v_decode()

    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 asthose 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 acombination 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 differentparents. Figure 4 illustrates two forms of this issue.

    ______________________________________________________________________________________________Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001

    Design Considerations for Using C++ in

    Safety Critical Avionics Systems

    Rev

    Page 14 of 23

  • 8/7/2019 C++_Safety

    15/23

    Vehicle

    turn()Gunfighter

    draw()

    Graphic

    draw()

    Gunfighter Graphic

    Airplane

    turn()

    Seaplane

    Boat

    turn()

    Figure 4 Redefinition via Multiple Paths

    MITIGATION:

    The left half ofFigure 4 demonstrates the first form of redefinition. In this form, two orthogonal concepts, agunfighter

    and agraphic symbol, are combined to form a new class. The problem is that the two orthogonal concepts share acommon 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 ofFigure 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 multipleinheritance 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 theseaplane class. The language requires this if it

    is desired to substitute aseaplane 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 theseaplane class and

    define how to turn aseaplane. 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 ofvehicle attributes in aseaplane). In C++,

    the default behavior is for the resulting object to contain one set of base class attributes per inheritance path. In the

    example, eachseaplane 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

    Safety Critical Avionics Systems

    Rev

    Page 15 of 23

  • 8/7/2019 C++_Safety

    16/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 controlflow). 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

    attribute_1

    run()

    method_A()

    method_B()

    Derived_1

    attribute_2

    method_B()

    method_C()

    Derived_2

    attribute_3

    method_A()

    method_D()

    Pseudo Code for Base::run

    {

    method_A();

    method_B();

    }

    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 theclass 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 varioustypes. 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

    run() Method

    Base::method_A()

    Base::method_B()

    Base::method_A()

    Derived_1::method_B()

    Derived_2::method_A()

    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

    Safety Critical Avionics Systems

    Rev

    Page 16 of 23

  • 8/7/2019 C++_Safety

    17/23

  • 8/7/2019 C++_Safety

    18/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 thanone 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 theshape example in

    Figure 1, theshape class is the base class and thus its constructor is called first. After the shape part of the object is

    constructed, the circle (orsquare) portion is constructed by calling its constructor. For destruction, the process isreversed, with the circle (orsquare) portion destructed first, followed by theshape part. The construction and

    destruction process for all objects proceeds in this fashion, regardless of the number of classes in the inheritancehierarchy 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 objectsruntime type changes during the construction process. Again, with a circle object in theshape example, when theshape

    constructor completes, the object is ashape, but not yet a circle. When the circle constructor completes, the object is

    both ashape 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 theshape destructor running, it is ashape, 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 objects 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 objects 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

    Safety Critical Avionics Systems

    Rev

    Page 18 of 23

  • 8/7/2019 C++_Safety

    19/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 definingtheir 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 naturalexpression 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 classconstructor 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. Thesame 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

    Safety Critical Avionics Systems

    Rev

    Page 19 of 23

  • 8/7/2019 C++_Safety

    20/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 orconversion 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 processrequire 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 processrequire 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 theorder 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

    Safety Critical Avionics Systems

    Rev

    Page 20 of 23

  • 8/7/2019 C++_Safety

    21/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 globaland 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 differentcompiler.

    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 aspreprocessor 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 programmers 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 havethe 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 themethod 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 thecase, 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 themethod 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 ifstatements.

    ______________________________________________________________________________________________Honeywell International Inc. Honeywell Proprietary PN: 721-00887-0001

    Design Considerations for Using C++ in

    Safety Critical Avionics Systems

    Rev

    Page 21 of 23

  • 8/7/2019 C++_Safety

    22/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 thefollowing 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 datatypes 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 atemplate 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

    Safety Critical Avionics Systems

    Rev

    Page 22 of 23

  • 8/7/2019 C++_Safety

    23/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 normalmethods, 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 likewhen 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 thereview 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