5

I have macro that performs a division 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 triggers an error if x does not divide by 2.
The macro BYTES_TO_WORDS is called from different contexts, sometimes with x as a compile-time constant integer expression and other times with x as an integer expression that is resolved on runtime.

Is it possible to implement CHECK_ALIGNMENT such that it would perform static_assert when the macro is called with constant expression, and some runtime error check when the expression is not a compile-time constant?
I can change the macro definitions, but not the way the macro is called and used.

Here is a possible solution (that doesn't always work):

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

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

A better solution would be identifying whether the parameter is a compile time constant and using static_assert in such case, with a nice compile time error message. If the parameter does not represent a compile time constant, then check the alignment on runtime.

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


Clarification

There are some discussions in the comments regarding to why I'm using macros in C++ question.
The BYTES_TO_WORDS macro is in a header file which is included by various tools, 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 to 1 as they are not capable of handling constexpr, templates or even function calls.
When compiling this header with C++ compiler I would like to define CHECK_ALIGNMENT to something else, that would either trigger static_assert or runtime error when needed.
The definition of CHECK_ALIGNMENT can be any C++11 code (or C++14 that is supported by VS2015), can use templates, constexpr or whatnot.

Community
  • 1
  • 1
Amir Gonnen
  • 3,525
  • 4
  • 32
  • 61
  • Don't use macros for this. Instead, write functions that are constexpr. http://stackoverflow.com/questions/14041453/why-are-preprocessor-macros-evil-and-what-are-the-alternatives https://isocpp.org/wiki/faq/inline-functions#inline-vs-macros – Caleth Mar 07 '17 at 13:58
  • @Caleth Upon reading the question, I also immediately thought about `constexpr`. So far, I've been unable to implement the requested functionality using it. Could you perhaps show an implementation using `constexpr`? – Maarten Bamelis Mar 07 '17 at 14:01
  • @Caleth I need to use macros since the same macros are also used by different tools (not necessarily Visual Studio c++ compiler) which do not support constexpr or even functions for that matter. – Amir Gonnen Mar 07 '17 at 14:18
  • @AmirGonnen If you cannot use `constexpr` or other C++11 features, it might be better to just remove the `c++11` tag from your question. If it **must** be implemented with macros, consider using the `macros` tag instead. – Maarten Bamelis Mar 07 '17 at 14:24
  • @MaartenBamelis The reason for using a separate `CHECK_ALIGNMENT` macro is to define it to 1 if the macro is not compiled with c++11 compiler. When compiling with c++11 compiler I would like to define this macro to some c++11 code that can trigger either static assert or runtime error. It's okay to use constexpr when defining `CHECK_ALIGNMENT`, but not avoiding macros altogether (when defining `BYTES_TO_WORDS`) – Amir Gonnen Mar 07 '17 at 14:29
  • @AmirGonnen You may want to add that explanation to your question, as it clearly explains your reasoning for messing with macros in the first place but also indicates that the implementation may use `constexpr` and other C++11 features. Still, I doubt that an implementation exists. – Maarten Bamelis Mar 07 '17 at 14:48
  • @MaartenBamelis I've added a clarification to my question, thanks for the advice. – Amir Gonnen Mar 07 '17 at 15:28
  • @AmirGonnen there is one topic that looks similar to your question. [compile-time and runtime detection](http://stackoverflow.com/a/40413051/2950316) . – Alexander Mar 07 '17 at 18:43

2 Answers2

0

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

So the code below "automatically" selects between a compile-time or runtime error as long as you remember to properly indicate compile-time constants by wrapping them. Note that the wrapper also automatically converts to a plain-old int thanks to the conversion operator.

Also, be sure to take a look at Boost.Hana before using the Constant class I created for the proof of concept implementation. The implementation presented below is not generic (it only wraps int and only converts back to int) and severely lacking in 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;
}
Maarten Bamelis
  • 2,243
  • 19
  • 32
  • Interesting! Why does the compiler prefer the `check_alignment(Constant)` over the `check_alignment(int value)` overload when provided with an integer literal (such as `BYTES_TO_WORDS(3)`)? – Amir Gonnen Mar 07 '17 at 16:03
  • @AmirGonnen It does not. That is a runtime check. Which is the main flaw of this implementation: the compile-time check only works [iff](https://en.wikipedia.org/wiki/If_and_only_if) you properly wrap your compile-time constants. – Maarten Bamelis Mar 07 '17 at 16:05
  • That's a no-go. I cannot change the code that uses `BYTES_TO_WORDS` macro. Event if I could, `BYTES_TO_WORDS(Constant<3>)` will not be evaluated correctly by the other tools (other than c++ compiler) that use the `BYTES_TO_WORDS` macro. – Amir Gonnen Mar 07 '17 at 16:08
  • @AmirGonnen In that case, I can be of no further help. I've tried quite some implementations but they would all require either nonexistent C++ features or modifications to the calling code. I cannot think of a way to automatically change code that calls the macro with an integer literal to a compile-time check. It seems that your best bet is getting that compiler bug fixed or switching to a different compiler. – Maarten Bamelis Mar 07 '17 at 16:20
  • @AmirGonnen Some people have [carefully constructed clever solutions](http://stackoverflow.com/a/40413051/4841248) to detect if a function is called with `constexpr` arguments or not. These are also not applicable to your problem because we cannot use a function argument in the condition of a static assert. This problem cannot be solved in C++ code until there is a feature to mark function arguments as `constexpr` (or something similar). And even then you would need to call the macro with an integer literal or an integer declared as `constexpr`. – Maarten Bamelis Mar 07 '17 at 17:17
  • If I could detect integer literals (and integer expressions made of integer literals) as the macro arguments, this could be good enough. I tried making [this](http://stackoverflow.com/a/40413051/619493) work on Visual Studio 2015 and see what I could make of it, but didn't have much success. – Amir Gonnen Mar 08 '17 at 07:15
0

There is a modern C++ solution (by SO user oliora, not by me) for giving a compile error when possible and a runtime error otherwise, which I will simply 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 is defined, you can create a function using it:

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

Side note: since this appears to be a utility for checking that the rightmost n bits are 0, I might take 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 that must be be usable in the BYTES_TO_WORDS expression, I'd use an #ifdef to ensure that 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 keep your original strategy of just returning 1 from the check_alignment function, which would eliminate the need for the lambda trick; alternatively, 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 details about constexpr_assert and several alternate approaches, see this blog post and this old answer of mine.

Community
  • 1
  • 1
Kyle Strand
  • 15,941
  • 8
  • 72
  • 167
  • 1
    For this to work, it requires the macro to be **called in a constexpr context** in order to get compile time static assert. For example, the statement `int i = divide(5, 2);` (in the original oliora gist) will assert **on runtime** not compile time. Since I cannot change every usage of my macro to constexpr, this solution does not solve my problem. I'll add another clarification in my question (that I can't change the macro usage, only the definition). – Amir Gonnen Mar 08 '17 at 06:39
  • @AmirGonnen Ah, you're right. However, I don't think it's possible to have well-defined behavior that changes depending on whether expressions are evaluated at compile-time or not, because compile-time evaluation is often an optional optimization, and optimizations are not supposed to change behavior. – Kyle Strand Mar 08 '17 at 07:25
  • I don't entirely agree. The compiler is well aware of whether the expression is constant or not. For example, you can only pass an integer constant (or constant expression) as a template parameter. This will work the same regardless of optimizations. – Amir Gonnen Mar 08 '17 at 07:53
  • @AmirGonnen Either I'm misunderstanding you, or you're misunderstanding me. I thought you were saying that my solution doesn't work for you because you'll get a runtime error when you'd prefer a compile time error, in contexts where the only way to get a compile time error is for the compiler to optimize by performing a computation during compilation that could equally validly be performed at runtime. Is that correct? If so, yes, the compiler knows whether it's doing the optimization or not, but I don't think it can behave differently. (Arguably this is a language shortcoming.) – Kyle Strand Mar 08 '17 at 08:13
  • However, if you can replace the macro entirely with a `constexpr` function (at least when compiling with C++), you will be able to use it in both constant and non-constant expression contexts. – Kyle Strand Mar 08 '17 at 08:17
  • I don't see how compiler optimization is relevant here. The compiler can detect whether an expression is an integer constant regardless of optimization. For example: `templatef()` then `f<40+2>` will compile and `f<40+i>` will not. Doesn't matter what optimization flags are. If this could be harnessed by SFAINE or some other mechanism to make the compiler select a different overload instead of emitting an error for `f<40+i> it could solve the problem. – Amir Gonnen Mar 08 '17 at 08:20
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/137559/discussion-between-kyle-strand-and-amir-gonnen). – Kyle Strand Mar 08 '17 at 13:38