Automatic selection between static assert and runtime error

I have a macro that performs splitting and checks alignment.

#define BYTES_TO_WORDS(x) ((CHECK_ALIGNMENT(x,2)) * ((x) / 2)) 

I would like to implement CHECK_ALIGNMENT as a macro that always returns 1 and throws an error if x does not divide by 2.
The BYTES_TO_WORDS macro BYTES_TO_WORDS called from different contexts, sometimes with x as a constant integer expression of compilation time, and another time with x as an integer expression that is allowed at runtime.

Is it possible to implement CHECK_ALIGNMENT so that it static_assert when the macro is invoked with a constant expression, and some execution check at runtime when the expression is not a compile-time constant?
I can change macro definitions, but not a way to call and use a macro.

Here is a possible solution (this does not always work):

 #define CHECK_ALIGNMENT(x,alignedTo) (1/(((alignedTo)-((x)%(alignedTo)))/(alignedTo))) 

In this implementation, we should get a Division By Zero error at runtime or compile time, depending on the input.
However, this does not always work due to a compiler error. Also, the error message is not very nice.

A better solution would be to determine if the parameter is a compile-time constant and uses static_assert in this case with a good compile-time error message. If the parameter is not a compile time constant, check alignment at run time.

Is it possible?
I need this to work with Visual Studio 2015.


Explanation

There are comments in the comments about why I use macros in a C ++ question.
The macro BYTES_TO_WORDS is located in the header file, which is included in various tools, the C ++ compiler is one of them.
Other tools use this macro and evaluate the arithmetic expression ((x) / 2) , but on these tools I define CHECK_ALIGNMENT - 1 because they are not able to handle constexpr , templates, or even function calls.
When compiling this header with a C ++ compiler, I would like to point CHECK_ALIGNMENT to something else that will cause a static_assert or a runtime error if necessary.
The definition of CHECK_ALIGNMENT can be any C ++ 11 code (or C ++ 14, which is supported by VS2015), it can use patterns, constexpr or whatnot.

+5
source share
2 answers

The only solution I can come up with requires that you exchange your compile-time constants before using them in the macros that you provided.

Therefore, the code below "automatically" chooses between a compile-time or a run-time error, if you remember to correctly specify compile-time constants by wrapping them. Note that the shell is also automatically converted to a regular int using the conversion operator.

Also, do not forget to take a look at Boost.Hana before using the Constant class that I created to prove the implementation of the concept.The implementation below is not general (it only wraps int and only converts back to int ) and lacks functionality.


 #include <iostream> #include <stdexcept> template <int I> struct Constant { constexpr Constant() = default; constexpr operator int () const { return I; } }; template <int I = 0> constexpr int check_alignment(Constant<I>) { static_assert(I % 2 == 0, "Error: not divisible by 2!"); return 1; } int check_alignment(int value) { if (value % 2 != 0) throw std::runtime_error("Error: not divisible by 2!"); return 1; } #define CHECK_ALIGNMENT(x) check_alignment(x) #define BYTES_TO_WORDS(x) ((CHECK_ALIGNMENT(x)) * ((x) / 2)) int main() { constexpr Constant<2> c2; constexpr Constant<3> c3; constexpr Constant<4> c4; // Compile-time checks: static_assert(BYTES_TO_WORDS(c2) == 1, "Error!"); static_assert(BYTES_TO_WORDS(c2) == 2, "Error!"); // "Error!" static_assert(BYTES_TO_WORDS(c3) == 2, "Error!"); // "Error: not divisible by 2!" static_assert(BYTES_TO_WORDS(c4) == 2, "Error!"); static_assert(BYTES_TO_WORDS(c4) == 4, "Error!"); // "Error!" // Runtime checks: std::cout << BYTES_TO_WORDS(2) << std::endl; // outputs "1" std::cout << BYTES_TO_WORDS(3) << std::endl; // "Runtime error: Error: not divisible by 2!" std::cout << BYTES_TO_WORDS(4) << std::endl; // outputs "2" return 0; } 
0
source

There is a modern C ++ solution (from the user SO oliora, not me) to provide compilation when possible, and runtime errors otherwise I just copy here:

 // A compilation of the following posts: // https://stackoverflow.com/questions/18648069/g-doesnt-compile-constexpr-function-with-assert-in-it // http://ericniebler.com/2014/09/27/assert-and-constexpr-in-cxx11/ #include <cassert> #include <utility> template<class Assert> inline void constexpr_assert_failed(Assert&& a) noexcept { std::forward<Assert>(a)(); } // When evaluated at compile time emits a compilation error if condition is not true. // Invokes the standard assert at run time. #define constexpr_assert(cond) \ ((void)((cond) ? 0 : (constexpr_assert_failed([](){ assert(!#cond);}), 0))) 

One constexpr_assert , you can create it using the function:

 template<typename T> constexpr void check_alignment(T x, short alignTo) { constexpr_assert(x % alignedTo == 0); } 

Side note: since this seems like a utility for checking that the rightmost n bits are 0, I could use a different approach:

 template<typename T> constexpr void assert_rightmost_zeroes(T x, short zero_bits) { auto shifted = x >> zero_bits; shifted = x << zero_bits; constexpr_assert(x == shifted); } 

Then, for your actual macro to be used in the BYTES_TO_WORDS expression, I would use #ifdef to make sure you have two versions, one of which works with your non-C ++ tools:

 #ifdef __cplusplus // Create and invoke a lambda to turn a series of statements into a single expression # define CHECK_ALIGNMENT(x, alignTo) [](auto x, auto alignTo){ check_alignment(x, alignTo); return 1; }(x, alignTo) #else // Use the solution that doesn't always work and has an ugly error message # define CHECK_ALIGNMENT(x, alignedTo) (1/(((alignedTo)-((x)%(alignedTo)))/(alignedTo))) #endif 

(Of course, you could save your original strategy of only returning 1 from the check_alignment function, which would eliminate the need for a lambda trick, otherwise you could just have two different definitions for BYTES_TO_WORDS , one of which is a function, the other of which is a macro. )

For more on constexpr_assert and several alternative approaches, see this blog post and this old my answer .

0
source

Source: https://habr.com/ru/post/953283/


All Articles