在静态断言和运行时错误之间自动选择 [英] Automatically selecting between static assert and runtime error

查看:25
本文介绍了在静态断言和运行时错误之间自动选择的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个执行除法和检查对齐的宏.

I have macro that performs a division and checks alignment.

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

我想将 CHECK_ALIGNMENT 实现为一个总是返回 1 的宏,如果 x 没有被 2 除会触发一个错误.
BYTES_TO_WORDS 从不同的上下文中调用,有时使用 x 作为编译时常量整数表达式,其他时候使用 x 作为整数表达式这是在运行时解决的.

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.

是否可以实现 CHECK_ALIGNMENT 以便在使用常量表达式调用宏时执行 static_assert,并在表达式不是编译时执行一些运行时错误检查-时间常数?
我可以更改宏定义,但不能更改调用和使用宏的方式.

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)))

在这个实现中,我们应该在运行时或编译时得到 Division By Zero 错误,这取决于输入.
但是,由于 编译器错误.另外,错误信息也不是很好.

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.

更好的解决方案是确定参数是否是编译时常量,并在这种情况下使用 static_assert,并带有一个很好的编译时错误消息.如果参数不代表编译时常量,则在运行时检查对齐.

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.

这可能吗?
我需要它在 Visual Studio 2015 上工作.

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

评论中有一些关于为什么我在 C++ 问题中使用宏的讨论.
BYTES_TO_WORDS 宏位于头文件中,各种工具都包含该头文件,C++ 编译器就是其中之一.
其他工具使用此宏并计算算术表达式 ((x)/2),但在这些工具上,我将 CHECK_ALIGNMENT 定义为 1,因为它们无法处理 constexpr、模板甚至函数调用.
使用 C++ 编译器编译此头文件时,我想将 CHECK_ALIGNMENT 定义为其他内容,这将在需要时触发 static_assert 或运行时错误.
CHECK_ALIGNMENT 的定义可以是任何 C++11 代码(或 VS2015 支持的 C++14),可以使用模板,constexpr 或诸如此类.

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.

推荐答案

有一个 现代 C++ 解决方案(由 SO 用户 oliora 提供,不是 由我提供) 用于在可能的情况下给出编译错误,否则给出运行时错误,我将简单地复制到此处:

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)))

定义了一个constexpr_assert,你可以使用它创建一个函数:

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);
}

旁注:由于这似乎是用于检查最右边的 n 位是否为 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);
}

然后,对于必须在 BYTES_TO_WORDS 表达式中可用的实际宏,我将使用 #ifdef 来确保您有两个版本,其中一个适用于您的非 C++ 工具:

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

(当然,您可以保留仅从 check_alignment 函数返回 1 的原始策略,这将消除对 lambda 技巧的需要;或者,您可以为 BYTES_TO_WORDS,其中一个是函数,另一个是宏.)

(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.)

有关 constexpr_assert 和几种替代方法的更多详细信息,请参阅 这篇博文我的这个旧答案.

For more details about constexpr_assert and several alternate approaches, see this blog post and this old answer of mine.

这篇关于在静态断言和运行时错误之间自动选择的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆