2

In the example below, Y and X give a warning "variable has static storage duration and non-POD type" (pclint, Autosar A3-3-2).

struct Y {int t; Y() {t = 0;}};
class X {private: int t; public: X() {t = 0;}};
struct Z {int t;};

X x; // warning: variable 'x' has 'static' storage duration and non-POD type
Y y; // variable 'y' has 'static' storage duration and non-POD type
Z z;

I have 2 questions.

  1. What could happen before the constructor is called which justifies the warning?

Edit: In my case the global variable is only used in the standard namespace and data is accessed by global functions in this namespace. Therefore the constructor should be executed before data is accessed.

  1. How can I avoid the warning which occurs for Y and X? I want to avoid possible uninitialised state if used with automatic storage (as for Z), therefore I would like to keep the constructor or reach the goal somehow else.

One solution could be the use of a C++ wrapper class which would initialise the struct. Is there a simpler/alternative solution, where uninitialised use of member "int t" cannot occur?

Ernie Mur
  • 481
  • 3
  • 18
  • `pclint, Autosar A3-3-2` -- Wouldn't any "fixes" require knowledge of the `pclint` product? – PaulMcKenzie Jan 14 '21 at 13:59
  • I am just evaluating it for 1 day and I know that I could avoid that the warning is produced. But I would like to have a solution which is independent of pclint (maybe the warning is justified? - question 1). I am curious if there would be another solution using C++ and not parameters for the static checker. – Ernie Mur Jan 14 '21 at 14:02

1 Answers1

1

From the AutoSAR C++14 Guidelines:

Rule A3-3-2 (required, implementation, automated)

Static and thread-local objects shall be constant-initialized.

Rationale

In general, using non-const global and static variables obscures the true dependencies of an API, since they can be accessed from any place of the source code. It therefore makes the code more difficult to maintain, less readable, and significantly less testable.

A particular problem is that the order in which constructors and initializers for static variables are called is only partially specified by the C++ Language Standard and can even change from build to build. This can cause issues that are difficult to find or debug.

[...]


What could happen before the constructor is called which justifies the warning?

The general rationale for the rule is to avoid the complex rules of where dynamic initialization applies for static initialization, a common cause for e.g. the static initialization fiasco.


How can I avoid the warning which occurs for Y and X? I want to avoid possible uninitialised state if used with automatic storage (as for Z), therefore I would like to keep the constructor or reach the goal somehow else.

A simply fix for your examples is to make their constructors constexpr, such that the static storage duration objects x and y are constant-initialized instead of zero-initialized as part of static initialization:

Constant initialization is performed instead of zero initialization of the static and thread-local (since C++11) objects and before all other initialization. Only the following variables are constant initialized:

[...]

  1. Static or thread-local object of class type that is initialized by a constructor call, if the constructor is constexpr and all constructor arguments (including implicit conversions) are constant expressions, and if the initializers in the constructor's initializer list and the brace-or-equal initializers of the class members only contain constant expressions.

E.g. (moving the initialization of the t data member by a constant expression to its default member initializer):

struct Y {
    int t{0}; 
    constexpr Y() {}
};

class X {
 private: 
    int t{0}; 
 public: 
    constexpr X() {}
};

X x;  // static initialization is constant-initialization
Y y;  // static initialization is constant-initialization

or alternatively initializing the t member in the member initializer list of the constexpr constructor:

struct Y {
    int t; 
    constexpr Y() : t{0} {}
};

class X {
 private: 
    int t; 
 public: 
    constexpr X() : t{0} {}
};

Finally, note that POD is no longer a standardese term as of C++11 (deprecated), and as the AutoSAR C++14 Guidelines naturally applies for C++14, one should consider entirely dropping the notion of POD class types; this likewise applies for pclint.

dfrib
  • 70,367
  • 12
  • 127
  • 192
  • Unfortunately the warning does not disappear using any of your 4 variants. – Ernie Mur Jan 14 '21 at 14:42
  • What about if you used direct-braced-init? `X x{};`. – dfrib Jan 14 '21 at 15:11
  • Same behaviour. – Ernie Mur Jan 14 '21 at 15:29
  • @ErnieMur My own experience with pclint, from using it over several years on a C++03 code base, enforcing MISRA C++:2008, was it was not the best tool for the job. I also got the feeling they were slow to update for modern safety standards and language standards; the mention of a ”POD-type” for a static analysis tool intended for a safety standard based on C++14 is mumbojumbo. – dfrib Jan 14 '21 at 15:39
  • @ErnieMur You may want to look into and compare with other tools that have support for static analysis over AutoSAR C++14; e.g. Perforce’s Helix QAC, Synopisis’ Coverity, and others. LDRA is another option, but last I tried it (some 2 years ago) it was very poor on any non-C like C++ (couldn’t handle templates at all). – dfrib Jan 14 '21 at 15:41