Clang-Reorder-Fields#

clang-reorder-fields is a refactoring tool to reorder fields in C/C++ structs and classes. This tool automatically updates:

  • Field declarations in the record definition

  • Constructor initializer lists in C++ classes

  • Aggregate initialization expressions (both C and C++)

  • Designated initializer lists (C++20)

This can be useful for optimizing memory layout, improving cache performance, or conforming to coding standards that require specific field orderings.

Example usage#

Basic struct reordering#

Consider this simple struct in example.c:

struct Foo {
  const int *x;
  int y;
  double z;
  int w;
};

int main() {
  const int val = 42;
  struct Foo foo = { &val, 0, 1.5, 17 };
  return 0;
}

To reorder the fields to z, w, y, x, run:

clang-reorder-fields -record-name Foo -fields-order z,w,y,x example.c --

This will reorder both the struct definition and the initialization:

struct Foo {
  double z;
  int w;
  int y;
  const int *x;
};

int main() {
  const int val = 42;
  struct Foo foo = { 1.5, 17, 0, &val };
  return 0;
}

Namespaced structs#

For C++ code with namespaces, use the fully-qualified name:

namespace bar {
struct Foo {
  const int *x;
  int y;
  double z;
  int w;
};
}
clang-reorder-fields -record-name ::bar::Foo -fields-order z,w,y,x example.cpp --

For classes defined in the global namespace (without any namespace), you can use either the simple class name or prefix it with :::

clang-reorder-fields -record-name Foo -fields-order z,w,y,x example.cpp --
# or
clang-reorder-fields -record-name ::Foo -fields-order z,w,y,x example.cpp --

C++ constructor initializer lists#

The tool also reorders constructor initializer lists. Given:

class Foo {
public:
  Foo();

private:
  int x;
  const char *s1;
  const char *s2;
  double z;
};

Foo::Foo():
  x(12),
  s1("abc"),
  s2("def"),
  z(3.14)
{}

Running:

clang-reorder-fields -record-name Foo -fields-order s1,x,z,s2 example.cpp --

Will reorder both the field declarations and the constructor initializers:

class Foo {
public:
  Foo();

private:
  const char *s1;
  int x;
  double z;
  const char *s2;
};

Foo::Foo():
  s1("abc"),
  x(12),
  z(3.14),
  s2("def")
{}

Designated initializers#

For C++20 code using designated initializers:

struct Bar {
  char a;
  int b;
  int c;
};

int main() {
  Bar bar1 = { 'a', 0, 123 };
  Bar bar2 = { .a = 'a', .b = 0, .c = 123 };
  return 0;
}
clang-reorder-fields --extra-arg="-std=c++20" -record-name Bar \
  -fields-order c,a,b example.cpp --

Will produce:

struct Bar {
  int c;
  char a;
  int b;
};

int main() {
  Bar bar1 = { 123, 'a', 0 };
  Bar bar2 = { .c = 123, .a = 'a', .b = 0 };
  return 0;
}

In-place editing#

Use the -i flag to modify files in-place:

clang-reorder-fields -record-name Foo -fields-order z,w,y,x -i example.c --

Limitations and Caveats#

Different access specifiers#

The tool cannot reorder fields with different access specifiers (public/private/protected). All fields being reordered must have the same access level.

class Example {
private:
  int x;
public:
  int y;  // Cannot reorder x and y - different access levels
};

Multiple field declarations#

Declarations with multiple fields in one statement are not supported:

struct Example {
  int a, b;  // Not supported - multiple fields in one declaration
};

Macro-expanded fields#

Macros that expand to multiple field declarations are not supported. However, macros that expand to a single field declaration work correctly:

#define INT_FIELD(NAME) int NAME     // Supported - expands to one field
#define TWO_FIELDS int a; int b;     // Not supported - expands to two fields

struct Supported {
  INT_FIELD(x);  // OK - this is a single field
  int y;
  INT_FIELD(z);  // OK - this is a single field
};

struct NotSupported {
  TWO_FIELDS     // Not OK - expands to multiple fields
  int c;
};

The tool can reorder fields declared via macros as long as each macro invocation expands to exactly one field declaration.

Preprocessor directives#

Structs with preprocessor directives between fields cannot be reordered:

struct Example {
  int a;
#ifdef FEATURE
  int b;
#endif
  int c;  // Not supported - preprocessor directives present
};

Flexible array members#

In C, a flexible array member is an incomplete array type that must be the last member of a struct (as specified by C99 and later standards). This allows the struct to have a variable-length array at the end. Since this is a language requirement, the tool enforces that flexible array members remain in the last position:

struct Example {
  int count;
  int data[];  // Flexible array member - must remain last
};

Attempting to reorder fields such that the flexible array member is no longer last will result in an error:

clang-reorder-fields -record-name Example -fields-order data,count example.c --

Will produce:

Flexible array member must remain the last field in the struct

This ensures the generated code remains valid C.

Field dependencies in initializers#

The tool will issue a warning if reordering causes a field to be used in an initializer before it’s initialized. Consider this example:

class Foo {
public:
  Foo(int x, char c);
  int x;
  char c;
  Dummy z;
};

Foo::Foo(int x, char c) :
  x(x),
  c(c),
  z(this->x, c)  // z's initializer uses x and c
{}

If you reorder the fields to z, c, x:

clang-reorder-fields -record-name Foo -fields-order z,c,x example.cpp --

The tool will produce warnings:

example.cpp:10:3: warning: reordering field x after z makes x uninitialized when used in init expression
example.cpp:10:3: warning: reordering field c after z makes c uninitialized when used in init expression

This warns you that in C++, member initializers are executed in the order that fields are declared in the class, not the order they appear in the initializer list. After reordering, z would be initialized first, but its initializer tries to use x and c which haven’t been initialized yet.

The tool will still perform the reordering but warns about the potential issue. You should review these warnings and adjust your code accordingly.

clang-reorder-fields Command Line Options#

--record-name=<string>#

The fully-qualified name of the struct or class to reorder. Required.

For C structs, use the struct name directly (e.g., Foo).

For C++ classes/structs in namespaces, use the fully-qualified name including namespaces (e.g., ::namespace::ClassName).

For C++ classes/structs in the global namespace, you can use either the simple name (e.g., Foo) or prefix with :: (e.g., ::Foo).

--fields-order=<string>#

Comma-separated list of field names in the desired order. Required.

All field names must exactly match the fields in the struct/class definition. The number of fields must match the number in the definition.

-i#

Overwrite edited files in-place. If not specified, the rewritten code is printed to stdout.

--extra-arg=<string>#

Additional argument to append to the compiler command line.

Useful for specifying language standards (e.g., --extra-arg="-std=c++20").

--extra-arg-before=<string>#

Additional argument to prepend to the compiler command line.

-p <string>#

Build path. Specifies the directory containing compile_commands.json for compilation database support.

Use Cases#

Memory layout optimization#

Reorder fields to minimize padding and improve cache locality:

// Before: 24 bytes (with padding)
struct Data {
  char a;     // 1 byte + 7 padding
  double b;   // 8 bytes
  char c;     // 1 byte + 7 padding
};
clang-reorder-fields -record-name Data -fields-order b,a,c data.c --
// After: 16 bytes (less padding)
struct Data {
  double b;   // 8 bytes
  char a;     // 1 byte
  char c;     // 1 byte + 6 padding
};

Coding standard compliance#

Ensure fields are ordered according to project conventions (e.g., alphabetically, by type, or by access pattern).

Field grouping#

Group related fields together for better code organization and readability.