Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Wordsmithing

...

In this noncompliant code example, a nonstandard-layout type object is passed across execution boundaries. The object type is defined in a header file used by both a library and an application. The application creates an instance of the object, then passes a reference to the object to a function defined by the library. However, if in this code example, the layout is not guaranteed to be compatible across execution boundaries because the compilers do not conform to the same ABI, and so it is a portability issue likely resulting that results in unexpected behavior.

Code Block
bgColor#FFCCCC
langcpp
// library.h
struct S {
  virtual void f() { /* ... */ }
};
 
void func(S &s); // Implemented by the library, calls S::f()
 
// application.cpp
#include "library.h"
 
void f() {
  S s;
  func(s);
}

...

If the library and the application conform to the same ABI, either explicitly through vendor documentation or implicitly by virtue of using the same compiler version to compile both the library and the application, then the noncompliant code example is instead a compliant solution. However, if because the library and application do not conform to the same ABI, the compliant solution requires modification of the library and application to work with a standard-layout type. This compliant solution adds a static_assert() to help guard against future code changes accidentally modifying S to no longer be a standard-layout type.

Page properties
hiddentrue

This CS is incredibly unsatisfying, but is still true.

Code Block
bgColor#ccccff
langcpp
// library.h
#include <type_traits>

struct S {
  void f() { /* ... */ } // No longer virtual
};
static_assert(std::is_standard_layout<S>::value, "S is required to be a standard layout type");

void func(S &s); // Implemented by the library, calls S::f()

// application.cpp
#include "library.h"

void f() {
  S s;
  func(s);
}

Noncompliant Code Example

In this noncompliant code example, std::memset() is used to clear the internal state of an object that is not of a standard-layout type. An implementation may store a vtable within the object instance, which is subsequently overwritten by the call to std::memset(), leading to undefined behavior when virtual method dispatch is required.

Code Block
bgColor#FFCCCC
langcpp
#include <cstring>

struct S {
  int i, j, k;
 
  // ...

  virtual void f();
};

void f() {
  S *s = new S;
  // ...
  std::memset(s, 0, sizeof(S));
  // ...
  s->f();
}

Compliant Solution

In this compliant solution, the data members of S are cleared explicitly instead of calling std::memset():

Code Block
bgColor#ccccff
langcpp
struct S {
  int i, j, k;
 
  // ...

  virtual void f();
  void clear() { i = j = k = 0; }
};

void f() {
  S *s = new S;
  // ...
  s->clear();
  // ...
  s->f();
}

Noncompliant Code Example

In this noncompliant code example, a pointer to an object of nonstandard-layout type is passed to a function that has a "Fortran" language linkage. Language linkages other than "C" and "C++" are conditionally supported with implementation-defined semantics [ISO/IEC 14882-2014]. If the implementation does not support this language linkage, the code is ill-formed. Assuming that the language linkage is supported, any operations performed on the object passed may result in indeterminate behavior, which could have security implications.

Code Block
bgColor#FFCCCC
langcpp
struct B {
  int i, j;
};
 
struct D : B {
  float f;
};
 
extern "Fortran" void func(void *);
 
void f(D *d) {
  func(d);
}

Compliant Solution

In this compliant solution, the nonstandard-layout type object is serialized into a local standard-layout type object, which is then passed to the Fortran function:

...