2026-02-24

c return status

specification of a c status-return convention and a centralized cleanup pattern compatible with it.

the convention defines:

  • structure and meaning of status_t
  • function return obligations
  • caller propagation semantics
  • a structured cleanup region form

status value

type

typedef struct {
  int id;
  const char* group;
} status_t;

semantics

  • id == 0 denotes success
  • id != 0 denotes failure
  • group identifies the namespace defining id

identity

canonical success value:

{ .id = 0, .group = "" }

global identification

a status is identified by the pair:

(group, id)

function contract

return rule

each fallible function:

  • returns status_t
  • returns id == 0 on success
  • returns id != 0 on failure
  • sets group to the namespace that defines the returned id

local carrier

a function may declare:

status_t status = {0, ""};

and return that variable at any return site.

multiple return statements are permitted.

propagation contract

status-returning call

given:

status = callee(...);

if:

status.id != 0

the caller determines control flow based on that condition and may propagate the same status value.

propagation preserves both group and id unless explicitly reassigned.

integer interoperability

for a function returning an integer error code:

int code = other_function(...);

lift into a status value:

status.id = code;
status.group = "";

where:

  • code == 0 denotes success
  • code != 0 denotes failure

centralized cleanup structure

cleanup region

a function that manages resources may define a labeled region:

exit:

control flow may transfer to this region when a failure condition is detected.

normal execution may also reach this region.

resource preparation pattern

for resources that require cleanup on failure:

  • a variable representing the resource is initialized to a defined neutral value before acquisition
  • acquisition updates that variable to a prepared state
  • the cleanup region releases the resource only if it is in the prepared state

the method used to encode prepared versus non-prepared state is implementation-defined.

cleanup semantics

at exit::

  • each resource that is in prepared state is released
  • no resource is released more than once
  • the function returns the final status value

conformance

a module conforms when:

  • its fallible apis return status_t
  • it uses id == 0 as the success indicator
  • it treats (group, id) as the failure identifier
  • callers determine success or failure solely by inspecting status.id

example

#include "sph/status.c"

status_t test() {
  status_declare;
  if (1 < 2) {
    status_set_goto("mylib", 456);
  }
exit:
  return status;
}

int main() {
  status_init;
  // code ...
  status_require(test());
  // more code ...
exit:
  return status.id;
}

implementation

status.h

#ifndef sph_status_h_included
#define sph_status_h_included

/* return status as integer code with group identifier
for exception handling with a local variable and a goto label
status id 0 is success, everything else can be considered a special case or failure
status ids are signed integers for compatibility with error return codes from other existing libraries
group ids are strings, used to categorise sets of errors codes from different libraries for example */
#include 
typedef struct {
  int id;
  char* group;
} status_t;
#define status_id_success 0
#define status_group_undefined ""
#define status_declare status_t status = { status_id_success, status_group_undefined }
#define status_is_success (status_id_success == status.id)
#define status_is_failure !status_is_success
#define status_return return (status)
#define status_i_return return ((status.id))
#define status_goto goto exit
#define status_set(group_id, status_id) \
  status.group = group_id; \
  status.id = status_id
#define status_set_goto(group_id, status_id) \
  status_set(group_id, status_id); \
  status_goto
#define status_require(expression) \
  status = expression; \
  if (status_is_failure) { \
    status_goto; \
  }
#define status_i_require(expression) \
  status.id = expression; \
  if (status_is_failure) { \
    status_goto; \
  }
#define status_require_return(expression) \
  status = expression; \
  if (status_is_failure) { \
    status_return; \
  }
#endif

further reading

error handling in general