Search code examples
c++contractc++20

Testing against the C++20 contracts (assertions)


Herb Sutter delivered a talk at ACUU Conference regarding the future of exceptions in C++ and upcoming contracts to replace or enhance existing assert.

He postulates the following rules to deal with error handling:

  1. System corruption (e.g., stack overflow): terminate

  2. Programming bug (e.g. precondition violation): assertions, contracts

  3. Recoverable error (e.g., network down): exception, error code

While I agree, I am not using this approach and essentially combine the 2 and 3 into one. I do that because I do not know how to test assertions or the upcoming contracts. Both use similar mechanisms that their violation causes the program to terminate and call optional handlers before that.

From the documentation (emphasis is mine):

A program may be translated with one of two violation continuation modes:

  • off (default if no continuation mode is selected): after the execution of the violation handler completes, std::terminate is called;
  • on: after the execution of the violation handler completes, execution continues normally.

Implementations are encouraged to not provide any programmatic way to query, set, or modify the build level or to set or modify the violation handler.

This means that test programs that will be tested against the contracts will require an extra compilation switch. While I dislike this, I understand why it would be done this way. More worrying is the last part, and the question about it has been raised. If the ability to set our own contract violation handler is implementation-defined, it will not be consistent across toolchains. Any tests of the contracts will, therefore, not be portable if at all possible. Setting it as yet another parameter to the linker would be quite awkward as well (and definitely not portable).

Currently, testing against assert is not possible because it simply aborts the program, and the custom handler cannot prevent that as far as I know. With contracts, this was supposed to be possible using the custom handler and the build switch, but as it turns out, it might not be possible as well (or be implementation-defined).

Or is there any other way testing against asserts and contracts is possible that I am missing?


Solution

  • There isn't much implementation variance with regard to contract violations. The "build level" determines whether contract checking happens at all. The violation handler will be called on any checked contracts that fail.

    The prototype for the handler is defined by the specification. The only implementation variance with regard to the violation handler is how you set it. Note that "implementation-defined" does not mean "does not allow this to happen". That is, the standard says that there will be a mechanism to establish the handler, and that this mechanism will be documented with the implementation (that's what "implementation-defined" means, as opposed to merely "unspecified"). Exactly what that mechanism is will be up to the implementation, but not providing one is not an option.

    The only real problem that could arise is if more than one implementation decides to allow users to specify the handler by blessing a specific global name, and if these implementations use a different global name. However, given that picking a global name is a bad idea for many reasons (conflicts with existing code, macros, etc), it's unlikely that this will be a problem in reality. So more likely than not, they'll choose the obvious mechanism: a compiler switch that specifies the name of the function to use as a handler.

    Note that one of the reasons to make the definition of the handler static rather than runtime-defined is to make sure that the switch is a compiler switch rather than a linker switch. The compiler is the one that emits any concept-checking code, after all. So the compiler can just use the name of the function in question in said checking code.

    Oh sure, different compilers may/will have different compiler switches for establishing which function gets called. But they already have different switches for generating debug info, optimization levels, and basically everything else. So whatever cross-platform testing system you're using will already need to be able to handle such distinctions. The violation handler is just one more.