Christopher Kohlhoff wrote:
Why must this exist? (With the emphasis placed on "generic".)
For the reason I stated; so that one can write a function whose logic is not broken when a callee switches (or is switched, or is ported) to another implementation and starts returning error codes from a different domain.
This is not logical. Part of being able to switch backends is that both backends adhere to a given specification. In the error handling model in your example, this specification would stipulate that on failure a function produces a non-zero-valued error code. Producing the errors from a different domain doesn't change that fact. However, this property is independent of the error_code itself; it is a property of your specification.
This is circular reasoning. My specification would return success when the function succeeds. How would it return success is determined by what is the established way to return success. The current de-facto standard way of returning success is zero, so I'd specify my function to return that. Were the standard way to signal success something else, my specification would reflect that something else.
Originally you phrased it as a generic way to test for failure, but I think I mostly agree with you if you instead phrase it in terms of success (as you are now doing).
These two ways to phrase it are the same; generic success and generic failure to succeed are complementary. Today, the logic is expressed in terms of if(ec) and if(!ec); whether the function tests for failure do_f1( ec ); if( ec ) return; do_f2( ec ); or for success do_f1( ec ); if( !ec ) do_f2( ec ); makes no difference.