Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: minor editorial changes

Standard-layout types can be used to communicate with code written in other programming languages, as the layout of the type is strictly specified. The C++ Standard, [class], paragraph 7 [ISO/IEC 14882-2014], defines standard-layout classes as ones a class that

  • Do Does not have virtual functions,
  • Have Has the same access control for all nonstatic data members,
  • Have Has no base classes of the same type as the first nonstatic data member,
  • Have Has nonstatic data members declared in only one class within the class hierarchy, and
  • Recursively, do does not have nonstatic data members of nonstandard-layout type.

An execution boundary is the delimitation between code compiled by differing compilers, including different versions of a compiler produced by the same vendor. For instance, a function may be declared in a header file but defined in a library that is loaded at runtime. The execution boundary exists between the call site in the executable and the function implementation in the library. Such boundaries are also called ABI (application binary interface) boundaries because they relate to the interoperability of application binaries.

...

A special instance of this guidance involves non-C++ code compiled by a different compiler, such as C standard library implementations that are exposed via the C++ standard library. C standard library functions are exposed with C++ signatures, and the type system frequently assists in ensuring that types match appropriately. This process disallows passing a pointer to a C++ object to a function expecting a char * without additional work to suppress the type mismatch. However, some C standard library functions accept a void * for which any C++ pointer type will suffice. Passing a pointer to a nonstandard-layout type in this situation results in indeterminate behavior because it depends on the behavior of the other language as well as on the layout of the given object. For more information, see rule EXP56-CPP. Do not call a function with a mismatched language linkage.

Only pass Pass a nonstandard-layout type object across execution boundaries when only when both sides of the execution boundary adhere to the same ABI. This is permissible if the same version of a compiler is used to compile both sides of the execution boundary, or if the compiler used to compile both sides of the execution boundary is ABI-compatible across multiple versions, or if the differing compilers document that they adhere to the same ABI.

...

This noncompliant code example assumes there is a library , whose header is library.h, and an application (represented by application.cpp), and that the library and application are not ABI-compatible. Therefore, the contents of library.h constitute an execution boundary. A nonstandard-layout type object S is passed across this execution boundary. The application creates an instance of an object of this type, then passes a reference to the object to a function defined by the library, crossing the execution boundary. Because the layout is not guaranteed to be compatible across the boundary, this 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 g() {
  S s;
  func(s);
}

Note that this This example would be compliant if the library and the application conform conformed to the same ABI, either explicitly through vendor documentation or implicitly by virtue of using the same compiler version to compile both.

...

Because the library and application do not conform to the same ABI, this compliant solution modifies the library and application to work with a standard-layout type. Furthermore, it also adds a static_assert() to help guard against future code changes that accidentally modifying modify S to no longer be a standard-layout type.

...

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:.

Code Block
bgColor#ccccff
langcpp
struct B {
  int i, j;
};

struct D : B {
  float f;
};

extern "Fortran" void func(void *);

void foo(D *d) {
  struct {
    int i, j;
    float f;
  } temp;
 
  temp.i = d->i;
  temp.j = d->j;
  temp.f = d->f;

  func(&temp);
}

...