What is your returns handling policy?

Huh? Ok, we shall talk about that later. Let’s build a case for modern strcmp first. Step by step.

strcmp is more than 40 years old.  Then and now, there are people who subscribe to the following: “This is how strcmp works. It is documented. If you use it wrongly, it is your problem.

First thing first. Repository with real crt_proxy_lib is here.

Canonical strcmp usage.

There you have it. A little strcmp primer. What could possibly go wrong? Well, there is also you people who can not afford crashes and wrong results when an empty string or null is used. And now the crucial point: the bugs are here to stay. Whoever is writing the strcmp calling side, you or anyone else. Feasible code must limit the chances for bugs as much as possible. You are one “clever lemming” and you know what to do.

So, you write a proxy function.

A proxy to strcmp. And lo and behold, it works and it is useful. But you use qsort too. Not a problem, just a second proxy function. Weeks are passing. Suddenly there are dozens or even hundreds of these proxies you or whoever else has written.  All added through time. For everybody to re-use.

Reuse is important. Reuse saves money. Reuse of proper stcmp proxy saves a lot of money. Money that will be spent on work-hours spent in debugging naked strcmp and many other legacy API calls dotted around.

And naturally, all this has been dropped into yet another library; let’s call it crt_proxy_lib. And by now, it is used everywhere in the whole company. In many teams. No good old CRT shenanigans any more, in here! Put out the flags and rejoice! Ok. Now please pause for a brief moment. Every software development company has crt_proxy_lib. In one form or another. My question is:

What is the error handling policy implemented in your “crt_proxy_lib”?

And the answer in 90% of cases is: There is no “error handling policy”. Sometimes there is also a counter “argument”: Why should there be one? All of them functions are documented!

Ugh.

Back to square one

Let’s develop an “error handling policy”. Presumably before “crt_proxy_lib” is even started. And then use it in there.

Side note:

When std lib has started there was no std lib “error” handling policy in existence. Thus std lib has no standard error handling.

But here and now you can do it right. Let us think what exactly are we thinking about. First and foremost is the key realization. Error handling does not cover even half of the uses cases.

Returns handling is wider in scope vs errors handling.

There are all sorts of information that might be required, as a result of a function call.  Error is just one kind of information returned. Think call consumers, not just simple callers. I suggest you think about returns handling in an all-new way. And that kind of thinking about something is called a “paradigm”.

metastate

is a paradigm. It is just a formalization of capturing all sorts of states of information, as a result of calling a function. But, crucially in a uniform, simple and lightweight way. Presumably using C++.  For further reading please see P2192, written by humble me.

crt_proxy_lib returns handling policy

  1. there is no error returns there is metastate returns
  2. Each  crt_proxy_lib function returns metastate, carried over by common or particular implementation of valstat.
  3. Each call to a function from the crt_proxy_lib API, should be followed by capturing and processing metastates.

metastate is returns handling policy in your returns handling department.

The API side

New terminology and slight confusion. Let the code speak.  First the strcmp_proxy implementation. Taking care of implementing the policy we just described above.

First, we will declare an absolutely minimal template. To implement the above-mentioned valstat.

naming is hard and extremely important. valstat is perhaps a name easy to remember.

valstat = value + status
metastate = occupancy_of(value) AND occupancy_of(status)

occupancy_ofoperates on values, not types. It is just helping the explanation here, it does not need to exist in code. We shall use the valstat to make and carry around a metastate. We need no special, complex types. No hundreds of lines of complex C++. And no “cognitive requirements” to even start thinking about how to use something. Everyone will be able to fully comprehend valstat (and the rest) after reading this post. Let us proceed.

For our strcmp proxy we will use this valstat definition

Template definition is a type. We will optionally return an int and an optional status message. In reality that will be some status code and there will be code-to-message mechanism provided. Here we will focus just on the core implementation.

We simply code around strcmp anti-pattern. Using valstat we signal back the findings. Thus the caller has the full report, before what we call “metastate capturing”.

The Callers side

using crt_proxy_lib , consuming algorithms and idioms are not prescribed or mandated in any way. We know the strcmp proxy is returning valstat instance to signal the outcome in the shape of the metastate. We know we will not crash on null usage, and we know on using empty strings we will not get the OK back.

Imagine all of the CRT written in this benevolent forgiving way. It is not. Millions of man-years have been wasted on debugging CRT calls. Back to the metastate. The caller could have written that in a more condensed, not recommended way. Or not. It entirely depends on the caller.

Certainly not recommended. value is empty on bad usage. What about “reporting back” from the crt_proxy_lib caller? Why not. Let’s use metastate for that to.

That is one illustrative but naive code. Obviously here we are out of crt_proxy_lib jurisdiction. But metastate adopters are still using it; perhaps in all of their application.

Actively manage the complexity

Some adopters might say: Above is naive but fine and we understand. But we have lost the reason why strcmp proxy has failed. So why don’t we just return the valstat in the ERROR metastate? And that decision alone is raising the level of complexity of their application. Considerably. If not actively managing that complexity they might implement what is otherwise known as the “call stack”.

If analysing of what has happened, is required, by code or humans, it is much better to deploy some well-known logging mechanism. Not just to make your app simpler and more resilient but also to make it a good data-centre citizen. Server-side apps must have some mechanism in use to signal to users and administrators. Warmly suggested further reading is here.

Here and now I might suggest this innocently looking log_and_proceed function

I am sure you will make it a macro. That little thing alone will make for very simple and resilient logging.  Leading to very functional service to valstat users.

Thus the log is preserved with no changes to the API. With few more additions, one can use that to know where and what has happened.

Be vigilant and actively kill the weed of complexity in your applications.

The benefits

Basically this returns handling policy delivers resilient and safe applications which are cheaper to deliver and maintain. How exactly?

API consuming uniformity

Doing this for one function is surely overkill. But please do remember: your crt proxy lib might have hundreds of them. Eachcrt_proxy_libcaller will (sooner or later) be implemented in the same canonical way. Leading to coding idioms. And that is a good thing.

All four are not always required, of course. That leads to code which is easier to comprehend by humans. Each caller follows the same idiom.

Run-time malleability

crt_proxy_lib can “hit the floor running” while covering a very wide scope of run-time environments. From IoT, medical devices, flight control systems, all the way to full-blown mission-critical server-side applications or very demanding gaming systems. That API is not using throw/try/catch and is not using globals. Many of proxies in there might be compile-time capable and many are pure functions. Modern compilers will be free to make wonders in optimizing the code of the crt_proxy_lib.

Basically we have shown here one server-side enabled, resilient proxy to the CRT. That fact considerably raises the level of feasibility of your projects. That, in turn, means code which is easier to maintain. And code maintenance is by far the largest consumer of your time and money.

Language malleability

That same returns handling policy can be implemented in almost any programing language today. Your front and middle tiers are written in (very likely) node.js, or python, go, typescript or even WASM. It is obviously easy to implement the same metastate paradigm in any of them. Probably the most convincing example is JSON

Of course, there are no comments in JSON. Those inter-system messages, do carry the meta state information. And it can be processed by almost all languages in widespread use today. One can pass the metastates all the way from the back to the front, and back again. From the Cloud to the browser and back.

Returns handling department

I would like to know what is your returns handling policy. Or do you just take care of error returns? What are the real-life implications of using it? Is your code compiling slower as a result of using special return types? Is it faster or slower vs for example using exceptions? What are your run-time requirements? Etc.

How have you organised your returns handling department?

 

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.