How to Handle amongoc_status#

The amongoc_status type is an extensible status-reporting type, allowing different subsystems to share integral error code values and understand each other’s error conditions without universal coordination on the meaning of opaque integral values. A status object has two salient properties: A code, and a category. Generally, user code should not inspect the code without also respecting the category.

Checking for Errors#

To test whether a particular opaque status represents a failure condition, do not be tempted to inspect the code value directly. Instead, use amongoc_is_error():

amongoc_status some_status = some_function();

// BAD:
if (some_status.code) {
  // ...
}

// GOOD:
if (amongoc_is_error(some_status)) {
  // ...
}

While it is conventional that an integer value zero represents a successful status, it is possible that the status’ category may have more than one success code value, and they may be non-zero. amongoc_is_error() consults the status category to determine whether the status code indicates an error.

Obtaining an Error Message#

The status category also knows how to convert an integer value into a human-readable status message. The amongoc_message() function will obtain a C string that represents the message for a status:

const char* msg = amongoc_message(some_status, NULL, 0);
printf("Status message: %s\n", msg);

Supporting Dynamic Messages#

A status category may need to dynamically generate a message string based on the integer value. In order to do this, it needs space to write the message string. This is the purpose of the second and third parameters to amongoc_message():

char msgbuf[128];
const char* msg = amongoc_message(some_status, msgbuf, sizeof msgbuf);
printf("Status message: %s\n", msg);

The second parameter must be a pointer to a modifiable array of char, and the third parameter must be the number of characters available to be written at that pointer. If amongoc_message() needs to dynamically generate a message string, it will use the provided buffer and return a pointer to that same buffer. amongoc_message() might or might not use the message buffer to generate the string, so you should not expect the character array to be modified by amongoc_message(). Always use the returned C string as the status message.

Note

If you do not provide a writable buffer for the message, amongoc_message() may return a fallback string that loses information about the status, so it is best to always provide the writable buffer.

A Shorthand#

Because declaring a writable buffer and calling amongoc_message() is so common, a shorthand macro amongoc_declmsg is defined that can do this boilerplate in a single line:

amongoc_declmsg(msg_var, some_status);
printf("Status msesage: %s\n", msg_var);

Internally, this will declare a small writable buffer and call amongoc_message() for us. The first parameter is the name of a variable to be declared, and the second parameter is the amongoc_status to be inspected.

Easy Error Handling#

You may find yourself writing code like this, repeatedly:

amognoc_status status = some_function();
if (amongoc_is_error(status)) {
  amongoc_declmsg(msg, status);
  fprintf(stderr, "The function failed: %s\n", msg);
  return 42;
} else {
  fprintf(stderr, "The function succeeded\n");
  return 0;
}

A macro, amongoc_if_error, can be used to do this more concisely:

amongoc_if_error (some_function(), msg) {
  fprintf(stderr, "The function failed: %s\n", msg);
  return 42;
} else {
  fprintf(stderr, "The function succeeded\n");
  return 0;
}