When generating C code, please adhere to the following style guidelines to ensure the code is maintainable, debuggable, and easy to modify:
- Follow the C11 standard
- Use Allman brace style
- Conceptualize programming as a sequence of data transformations, where specific input data is processed step-by-step to produce desired output data. For example, a program that handles HTTP requests can be understood as taking an HTTP request string and current database state to generate an HTTP response string. This perspective prioritizes concrete reasoning over abstract design principles often associated with object-oriented programming (OOP) or 'clean code' paradigms.
- Avoid object-oriented programming and prioritize Plain Old Data (POD) structures and procedural solutions.
- De-prioritize encapsulation as a design goal, focusing instead on simplicity and efficiency.
- Prefer explicit error code returns while avoiding errno and exceptions
- Do not nest if statements at all. Every if must appear only at the top level of the function’s block scope. No if may appear inside the body of another if.
- No Early Returns: Avoid using return statements early in functions. Ensure the function follows a linear flow from start to finish so that cleanup code or other operations at the end (e.g., memory management) are always executed.
- Use Flags for Sequential Conditions: For conditions that depend on each other, define boolean flags to track whether each step should proceed. Update these flags sequentially and use them to control subsequent blocks of code, keeping the logic linear.
- Explicit Dependencies: Use flags to make dependencies between code sections clear. Declare variables in an order that reflects these dependencies, so rearranging code correctly triggers compiler errors (e.g., using an undeclared variable). For independent sections, ensure their guarding conditions have no references to other scopes, allowing free rearrangement.
- Facilitate Debugging and Modifications: Structure the code to flow linearly, making it easy to step through in a debugger and inspect flag states to see which conditions were met. This also allows adding code (e.g., logging or memory management) at the function’s start or end with confidence it will execute.
- Accept Slight Verbosity: This style may increase code length slightly, but the benefits in readability, maintainability, and debuggability outweigh the verbosity.
Examples of Sequential Conditions
Avoid this (nested ifs):
void some_function()
{
bool some_condition_a = other_func1();
if (some_condition_a)
{
bool some_condition_b = other_func2();
if (some_condition_b)
{
bool some_condition_c = other_func3();
if (some_condition_c)
{
do_thing();
}
}
}
}Avoid this (early returns):
void some_function()
{
bool some_condition_a = other_func1();
if (!some_condition_a)
return;
bool some_condition_b = other_func2();
if (!some_condition_b)
return;
bool some_condition_c = other_func3();
if (!some_condition_c)
return;
do_thing();
}Prefer this (flags with linear flow):
void some_function()
{
bool proceed_to_b = false;
bool some_condition_a = other_func1();
if (some_condition_a)
{
proceed_to_b = other_func2();
}
bool proceed_to_c = false;
if (proceed_to_b)
{
proceed_to_c = other_func3();
}
if (proceed_to_c)
{
do_thing();
}
}