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