testing-debugging · beginner · ~15 min

Defensive Return Code Checker

Inspect and accumulate return codes through function pointer callbacks safely.

Challenge

In production C pipelines, every intermediate step returns an operational status code. Failing to check return values allows errors to cascade silently until fatal corruption occurs.

Your Task

Implement:

int execute_pipeline(const int *inputs, size_t count, int (*stage)(int in), int *failures);

Rules

  1. If inputs == NULL, stage == NULL, failures == NULL, or count == 0, return -1.
  2. Initialize *failures = 0.
  3. For each integer in inputs[0..count-1], call stage(inputs[i]).
  4. If stage(...) < 0, increment *failures.
  5. If *failures > 0 after processing all items, return 1 (indicating pipeline had errors).
  6. If all stages returned >= 0, return 0.

Example

int step(int x) { return x >= 0 ? x * 2 : -1; }
int data[] = {1, 2, -3, 4};
int fails = 0;
int rc = execute_pipeline(data, 4, step, &fails); // returns 1, fails == 1

Input format

inputs: array of integers; count: size_t; stage: function pointer returning int; failures: output count of failed stages.

Output format

Returns 0 if all stages succeed, 1 if any stage returned negative, -1 on invalid inputs.

Constraints

Must process all items in order. Check for NULL pointers.

Starter code

#include <stddef.h>

/* Execute stage on each input. Count negative returns in *failures.
   Return 0 on all-pass, 1 if any failed, or -1 on invalid args. */
int execute_pipeline(const int *inputs, size_t count, int (*stage)(int in), int *failures) {
    (void)inputs; (void)count; (void)stage; (void)failures;
    return -1;
}

Common mistakes

Stopping at the first failure instead of counting all failures; forgetting to initialize *failures = 0.

Edge cases to handle

All pass returns 0 and failures 0; all fail returns 1 and failures == count; NULL arguments return -1.

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.