No Missing Field Initializers
Introduction
Recently, after I extended an interface, more specifically a struct, in a C++ program, I encountered some weird bugs. I tried to make my update backward compatible, so I did not quite understand why the bugs would happen.
It turned out that it was due to the new attribute that I added to the struct object was not initialized and it was extremely difficult to root cause this in an extremely large codebase. In retrospect, if the struct object was initialized at construction and the compiler warnings were enabled, I would have caught this bug early at the beginning of my development.
In this blog post, I will discuss the importance of initializing the struct object at construction and enabling the compiler warnings to catch the bugs early using a simplified toy example.
No Missing Field Initializers
This program is bug-free at runtime before the upgrade. After the upgrade, the program becomes problematic at runtime because the value of the new attribute variable is undefined.
The original program initializes the Date
object after the construction in the create_random_valid_date
function. If the Date
object is initialized at construction, with appropriate compiler warnings enabled, the compiler can catch the bug early.
1 |
|
Original Software
To compile the program before the upgrade, we will define the macro INITIALIZE_AFTER_CONSTRUCTION
to GCC.
1 | $ g++ partial_initialization.cpp -o partial_initialization -DINITIALIZE_AFTER_CONSTRUCTION |
Notice that in my case, the Date
object was initialized after the construction in the create_random_valid_date
function. The software is bug-free and runs fine. I did not have to check the implementation of the create_random_valid_date
function until I introduced the updates and encountered the bugs.
Upgraded Software
To update the interface, I added a new attribute day
to the struct Date
. To make it backward compatible as much as possible, I specifically add it to the end of the struct. With the new struct, specifically, the new attribute day
, I can start to do some new work with a Date
object.
To compile the program after the upgrade, we will define the macro UPGRADED
to GCC.
1 | $ g++ partial_initialization.cpp -o partial_initialization -DINITIALIZE_AFTER_CONSTRUCTION -DUPGRADED |
When I ran the program, it crashed. I did not understand why there will be bugs in my program, since I extended the interface struct in a way that is backward compatible on purpose. Because a valid date object was generated from the create_random_valid_date
function, I did not expect that the date object would be invalid. However, because the create_random_valid_date
function was not updated when the Date
struct was updated, the new attribute day
was not possible to be initialized. Therefore, in this case, after I introduced the updates, the value of the new attribute day
was not undefined and caused the bugs. It took me a very long time to finally realized that the valid_date
object was no longer valid after the updates.
Best Practices
Compiler Warnings
The root cause was that the new attribute day
was not initialized. If we enable the compiler warnings, can we catch this bug early? The answer is no, because the Date
object in the create_random_valid_date
function was initialized after the construction. Therefore, the compiler cannot catch this bug.
1 | $ g++ partial_initialization.cpp -o partial_initialization -DINITIALIZE_AFTER_CONSTRUCTION -Wall -Wextra -Werror |
Initialize at Construction
However, if we initialize the Date
object at construction in the create_random_valid_date
function, with the compiler warnings enabled, we can catch this bug early.
1 | $ g++ partial_initialization.cpp -o partial_initialization -DINITIALIZE_AT_CONSTRUCTION -Wall -Wextra -Werror |
Conclusions
To implement a C/C++ program, it is important to initialize a struct object with no missing field initializers at construction. The compiler warnings should also be enabled to catch the unexpected behaviors , espcially from the code that does not belong to the developer, caused by the update early.
References
No Missing Field Initializers
https://leimao.github.io/blog/No-Missing-Field-Initializers/