Coding Convention: Difference between revisions

From NCOT Wiki
Jump to navigationJump to search
Line 212: Line 212:
int find_entity_by_name(const char *name); // Returns -1 if not found
int find_entity_by_name(const char *name); // Returns -1 if not found
</syntaxhighlight>
</syntaxhighlight>
== 9. When to Use a Struct ==
Structs in C are used to group related data together into a single unit. Use them to improve clarity, reduce duplication, and logically organize your code.
=== 9.1 Use a Struct When ===
* '''You have related data items that conceptually belong together.'''
<syntaxhighlight lang="c">
// Good: Position is a single concept with multiple parts
typedef struct Position_t {
    int x;
    int y;
} Position_t;
</syntaxhighlight>
* '''A function takes or returns multiple values that are always used together.'''
<syntaxhighlight lang="c">
// Instead of passing x and y separately:
void move_to(int x, int y);
// Use:
void move_to(Position_t pos);
</syntaxhighlight>
* '''You want to encapsulate the state of a module, object, or entity.'''
<syntaxhighlight lang="c">
typedef struct Player_t {
    int id;
    char name[32];
    int health;
    int score;
} Player_t;
</syntaxhighlight>
* '''You want to make your code more self-documenting.'''
  Structs make data structures explicit and easier to reason about.
* '''You are modeling a real-world object or concept.'''
  For example: `Window_t`, `Vector2D_t`, `Enemy_t`, `Timer_t`, etc.
=== 9.2 Avoid Creating a Struct When ===
* '''The values are only used temporarily and locally in one function.'''
  In this case, keep them as local variables unless clarity improves by grouping.
* '''The data items are not conceptually related.'''
  Grouping unrelated values can reduce clarity.
* '''It will add unnecessary indirection or complexity.'''
  Avoid premature abstraction — keep it simple unless there's a clear benefit.
=== 9.3 Naming Guidance ===
* Use `PascalCase` + `_t` for struct type names.
* Use `lowercase_with_underscores` for member fields.
* Prefer descriptive member names over short or cryptic ones.
=== 9.4 Example: Before and After Struct ===
'''Before (repetitive, unclear):'''
<syntaxhighlight lang="c">
void render_entity(int x, int y, int width, int height, const char *texture);
</syntaxhighlight>
'''After (clear, modular):'''
<syntaxhighlight lang="c">
typedef struct Rect_t {
    int x;
    int y;
    int width;
    int height;
} Rect_t;
void render_entity(Rect_t bounds, const char *texture);
</syntaxhighlight>




[[Category:Programming]]
[[Category:Programming]]

Revision as of 13:17, 29 July 2025

C Coding Convention

This convention defines naming and style rules for writing clean, consistent, and self-documenting C code.

General Principles

  • Use lowercase_with_underscores for variables and functions.
  • Use PascalCase for struct and enum type names.
  • Use prefixes to namespace functions and globals by module/library.
  • Keep names brief but descriptive.

1. Enums

Type Name

  • Use PascalCase with an `_e` suffix.
typedef enum GameState_e {
    GAME_STATE_INIT,
    GAME_STATE_RUNNING,
    GAME_STATE_PAUSED,
    GAME_STATE_EXIT
} GameState_e;

Enum Values

  • Use ALL_CAPS with a common prefix for namespacing.

2. Structs / "Classes"

Type Name

  • Use PascalCase with a `_t` suffix.
typedef struct Player_t {
    int id;
    char name[32];
    float health;
} Player_t;

Member Names

  • Use lowercase_with_underscores.
  • Optionally prefix with struct abbreviation if needed (e.g. `pos_x`, `player_name`).

3. Global Variables

  • Use `g_` prefix.
  • Follow with lowercase_with_underscores.
int g_frame_counter;
bool g_is_debug_mode;
  • For internal linkage, use `static`:
static int g_last_error_code;

4. Functions

General Functions

  • Use lowercase_with_underscores.
  • Use verb_noun structure for clarity.
void init_game();
int calculate_damage(int weapon_id);

Library/Module Functions

  • Prefix with the module name (e.g. `audio_`, `player_`).
// In audio module
void audio_init();
void audio_play_sound(const char *name);

5. Constants / Macros

  • Use ALL_CAPS_WITH_UNDERSCORES.
#define MAX_PLAYERS 16
#define PI 3.14159f

6. File Naming

  • Use `module_name.c` and `module_name.h`.

Examples:

  • `player.c`, `game_state.h`, `audio_mixer.c`

7. Example Summary

// game_state.h
typedef enum GameState_e {
    GAME_STATE_INIT,
    GAME_STATE_RUNNING,
    GAME_STATE_PAUSED,
    GAME_STATE_EXIT
} GameState_e;

// player.h
typedef struct Player_t {
    int id;
    char name[32];
    float health;
    bool is_alive;
} Player_t;

// player.c
Player_t g_main_player;

void player_init(Player_t *player);
void player_take_damage(Player_t *player, float damage);

8. Return Value Guidelines

Well-defined return values make code more predictable and easier to debug. Follow these conventions:

8.1 Function Types

**Void functions**

Use when the function performs an action and no result needs to be reported.

void log_message(const char *msg);
**Value-returning functions**

Use to return computed values or status.

int calculate_score(int time_remaining, int enemies_defeated);

8.2 Return Codes for Status/Errors

**Success/Failure convention**

Return `0` for success, non-zero for failure. Use named `#define` or `enum` values for clarity.

// error_codes.h
#define ERR_OK          0
#define ERR_FILE_NOT_FOUND 1
#define ERR_INVALID_ARG     2
// file_loader.c
int file_load(const char *path) {
    if (!path) return ERR_INVALID_ARG;
    ...
    return ERR_OK;
}
**Boolean results**

Return `bool` if the result is true/false only. Use `<stdbool.h>`.

#include <stdbool.h>

bool player_is_alive(Player_t *player);

8.3 Output via Pointers

If a function needs to return multiple values, return one as the return value and others via pointers.

bool get_player_stats(int player_id, int *health_out, int *score_out);

Use `_out` or `_result` suffix for output parameters.

8.4 Document Return Meaning Clearly

Always document what return values mean in headers or comments.

/**
 * Loads a resource from disk.
 * 
 * @param path Path to the resource file.
 * @return ERR_OK on success, or an error code on failure.
 */
int resource_load(const char *path);

8.5 Optional Return Value Idioms

**Nullable pointers**

Return `NULL` on failure when returning pointers.

Texture_t *texture_load(const char *filename); // Returns NULL on failure
**Sentinel values**

Use a known invalid value (e.g. `-1`, `NULL`, `0`) if needed and document it clearly.

int find_entity_by_name(const char *name); // Returns -1 if not found

9. When to Use a Struct

Structs in C are used to group related data together into a single unit. Use them to improve clarity, reduce duplication, and logically organize your code.

9.1 Use a Struct When

  • You have related data items that conceptually belong together.
// Good: Position is a single concept with multiple parts
typedef struct Position_t {
    int x;
    int y;
} Position_t;
  • A function takes or returns multiple values that are always used together.
// Instead of passing x and y separately:
void move_to(int x, int y);

// Use:
void move_to(Position_t pos);
  • You want to encapsulate the state of a module, object, or entity.
typedef struct Player_t {
    int id;
    char name[32];
    int health;
    int score;
} Player_t;
  • You want to make your code more self-documenting.
 Structs make data structures explicit and easier to reason about.
  • You are modeling a real-world object or concept.
 For example: `Window_t`, `Vector2D_t`, `Enemy_t`, `Timer_t`, etc.

9.2 Avoid Creating a Struct When

  • The values are only used temporarily and locally in one function.
 In this case, keep them as local variables unless clarity improves by grouping.
  • The data items are not conceptually related.
 Grouping unrelated values can reduce clarity.
  • It will add unnecessary indirection or complexity.
 Avoid premature abstraction — keep it simple unless there's a clear benefit.

9.3 Naming Guidance

  • Use `PascalCase` + `_t` for struct type names.
  • Use `lowercase_with_underscores` for member fields.
  • Prefer descriptive member names over short or cryptic ones.

9.4 Example: Before and After Struct

Before (repetitive, unclear):

void render_entity(int x, int y, int width, int height, const char *texture);

After (clear, modular):

typedef struct Rect_t {
    int x;
    int y;
    int width;
    int height;
} Rect_t;

void render_entity(Rect_t bounds, const char *texture);