Enforcing function contract at compile time when possible

  • A+
Category:Languages

(this question was inspired by How can I generate a compilation error to prevent certain VALUE (not type) to go into the function?)

Let's say, we have a single-argument foo, semantically defined as

int foo(int arg) {     int* parg;     if (arg != 5) {         parg = &arg;     }      return *parg; } 

The whole code above is used to illustrate a simple idea - function returns it's own argument unless the argument is equal to 5, in which case behavior is undefined.

Now, the challenge - modify the function in such a way, that if it's argument is known at compile time, a compiler diagnostic (warning or error) should be generated, and if not, behavior remains undefined in runtime. Solution could be compiler-dependent, as long as it is available in either one of the big 4 compilers.

Here are some potential routes which do not solve the problem:

  • Making function a template which takes it's argument as a template parameter - this doesn't solve the problem because it makes function ineligible for run-time arguments
  • Making function a constexpr - this doesn't solve the problem, because even when compilers see undefined behavior, they do not produce diagnostics in my tests - instead, gcc inserts ud2 instruction, which is not what I want.

 


I got error with constexpr when used in constant expression for:

constexpr int foo(int arg) {     int* parg = nullptr;     if (arg != 5) {         parg = &arg;     }     return *parg; } 

Demo

We cannot know that argument value is known at compile type, but we can use type representing value with std::integral_constant

// alias to shorten name.  template <int N> using int_c = std::integral_constant<int, N>; 

Possibly with UDL with operator "" _c to have 5_c, 42_c.

and then, add overload with that:

template <int N> constexpr auto foo(int_c<N>) {     return int_c<foo(N)>{}; } 

So:

foo(int_c<42>{}); // OK foo(int_c<5>{}); // Fail to compile  // and with previous constexpr: foo(5); // Runtime error, No compile time diagnostic constexpr auto r = foo(5); // Fail to compile 

As I said, arguments are not known to be constant inside the function, and if_constexpr seems not possible in standard to allow dispatch, but some compiler provide built-in for that (__builtin_constant_p), so with MACRO, we can do the dispatch:

#define FOO(X) [&](){ /     if constexpr (__builtin_constant_p(X)) {/         return foo(int_c<__builtin_constant_p (X) ? X : 0>{});/     } else {/         return foo(X); /     } / }() 

Demo

Note: Cannot use foo(int_c<X>{}) directly, even in if constexpr, as there is still some syntax check.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: