Move modules under src/modules/ and public headers under include/, keeping dev/, tests/, and docs at the top level. Adds a placeholder for src/node/ where the final video node entry point will live. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.9 KiB
Conventions and Preferences
Language
- C11 throughout
- Target platform: Linux (V4L2, epoll, etc. are Linux-specific)
Naming
| Kind | Convention | Example |
|---|---|---|
| Structs | Title_Snek_Case |
struct App_Error, struct Frame_Header |
| Enums (type) | Title_Snek_Case |
enum Error_Code |
| Enum values | CAPITAL_SNEK_CASE |
ERR_NONE, ERR_SYSCALL |
| Functions | lower_snek_case |
v4l2_ctrl_open(), app_error_print() |
| Local variables | lower_snek_case |
frame_count, device_fd |
Constants (#define, const) |
CAPITAL_SNEK_CASE |
MAX_FRAME_SIZE |
| Module-level singletons | lower_snek_case |
(treated as functions) |
Identifiers are prefixed with their module name where disambiguation is needed (e.g. v4l2_ctrl_, media_ctrl_, transport_).
Types
No typedefs for structs
Always use the struct keyword at the call site. This makes composite types unambiguous without inspecting declarations.
/* correct */
struct App_Error err = app_error_ok();
void process(struct Frame_Header *header);
/* wrong */
typedef struct { ... } App_Error;
typedefs are acceptable for
- Enums:
typedef enum Error_Code { ... } Error_Code;— allows using the name withoutenumkeyword - Scalar aliases where the underlying type is genuinely irrelevant to the caller (e.g.
typedef uint32_t Frame_Id;)
Formatting
- Indentation: tabs (display width 4)
- Braces: always use block braces, including single-statement bodies
/* correct */
if (err.code != ERR_NONE) {
return err;
}
/* wrong */
if (err.code != ERR_NONE)
return err;
- Quotes: double quotes for string literals (C standard)
Error Handling
Errors are returned as struct App_Error values. Functions that can fail return struct App_Error directly, or take an out-parameter for the result alongside it.
Structure
/* modules/common/error.h */
typedef enum Error_Code {
ERR_NONE = 0,
ERR_SYSCALL = 1, /* errno is meaningful */
ERR_INVALID = 2,
} Error_Code;
struct Syscall_Error_Detail {
int err_no;
};
struct Invalid_Error_Detail {
/* fields added as concrete cases arise */
};
struct App_Error {
Error_Code code;
const char *file; /* __FILE__ — future: replaced by generated location code */
int line; /* __LINE__ — future: replaced by generated location code */
union {
struct Syscall_Error_Detail syscall;
struct Invalid_Error_Detail invalid;
} detail;
};
Macros
#define APP_OK \
((struct App_Error){ .code = ERR_NONE })
#define APP_IS_OK(e) \
((e).code == ERR_NONE)
#define APP_ERROR(error_code, detail_field, ...) \
((struct App_Error){ \
.code = (error_code), \
.file = __FILE__, \
.line = __LINE__, \
.detail = { .detail_field = { __VA_ARGS__ } } \
})
#define APP_SYSCALL_ERROR() \
APP_ERROR(ERR_SYSCALL, syscall, .err_no = errno)
Presentation
Each error kind is handled in app_error_print(), which writes a human-readable description to stderr. A JSON writer (app_error_write_json()) will be added later and will use the same structured fields.
Future upgrade
When the custom preprocessor is available, __FILE__ and __LINE__ will be replaced by a generated numeric location code that uniquely identifies the error site in the project. JSON consumers will see the same struct shape either way.
Directory Structure
video-setup/
src/ - all C source files
modules/ - translation units; each has a .c and a Makefile
common/ - shared types (error, base definitions); no external dependencies
<module>/
node/ - video node entry point and integration (later)
include/ - public headers (.h files)
dev/ - development aids; not part of the final deliverable
cli/ - exploratory CLI drivers, one per module
experiments/ - freeform experiments
tests/ - automated tests (later)
Makefile - top-level build
Source files live in src/. Public headers live in include/. CLI drivers and experiments live in dev/. Nothing in dev/ is a dependency of anything in src/.
Module Structure
Each module has its source under src/modules/<name>/ and its public header under include/:
src/modules/<name>/
<name>.c - implementation
Makefile - builds a static object
include/
<name>.h - public API; minimal includes, no implementation details
The corresponding CLI driver lives at dev/cli/<name>_cli.c.
Build
- GNU make with tabs for recipe indentation
- Each module builds to a static object (
.o) - CLI drivers link the module object(s) they need
- No CDN or vendored sources; dependencies are system libraries (libc, Linux kernel headers)