Return Codes

Unlike exceptions, return codes are explicit and visible. In fact, that visibility can be seen as a major drawback.

When I first encountered pervasive use of return codes, I was skeptical. Now I am a convert. By being visible throughout the source code, command cancelation is always in focus.


org::prj::StatusCode performAction(const InputData& data)
{
  const auto status1 = doA();
  if (status1 != org::prj::StatusCode::Good) {
    return status1;
  }
  const auto status2 = doB(data);
  if (status2 != org::prj::StatusCode::Good) {
    return status2;
  }
  const auto status3 = doC();
  if (status3 != org::prj::StatusCode::Good) {
    return status3;
  }
  return org::prj::StatusCode::Good;
}

With the proper use of macros, this can (visually) simplify to:


org::prj::StatusCode performAction(const InputData& data)
{
  CALL(doA());
  CALL(doB(data));
  CALL(doC());
  return org::prj::StatusCode::Good;
}

Now the question narrows to "how do I implement return codes?" A reasonable answer is `enum class` but this could have complications. Often such enumerations become a dependency magnet, being included (C++) or withed (Ada) from all over the codebase. A second issue falls into what I call gray code. This is where infrastructure code and application code must mix together in a single file (or in a single enumerated list).

What we want is something like an enumerated type, but also like exceptions where we can add to the universe of values by adding leaves to the tree. The new leaves do not have to be defined in the central (dependency magnet) place, and can refine existing values.

See https://github.com/davidkristola/fluent-hsm-cpp/tree/master/kv/status

.seealso Cross-Cutting Concerns .seealso Macros .seealso Namespaces

Introduction