我正在寻找一种从constexpr函数中引发编译时错误的方法。由于我在嵌入式系统上,因此需要保持C ++异常禁用(GCC标志-fno-exceptions)。因此,错误报告的默认方式似乎是不可行的。
constexpr error at compile-time, but no overhead at run-time中描述的一种可能的方法是调用非constexpr函数,如果强制编译时实现则抛出错误。但是,此解决方案提供了相当不可读的错误消息,并且强制实现强制返回垃圾返回值,以便“控制可能达到无效函数结束”警告。
是否有更好的方法,可以提供自定义错误消息?
请注意,我知道static_assert
以及将该功能转换为模板的可能性。但是,static_assert
需要重新组合我的用例的开关块的相当复杂的逻辑,以便抛出错误,这容易出错并且笨拙。
示例用例:
constexpr SpiDmaTxStreams spiDmaTxStream(DmaId dmaId, DmaStreamId streamId) {
switch (dmaId) {
case DmaId::DMA_1:
switch (streamId) {
case DmaStreamId::Stream_4:
return SpiDmaTxStreams::Dma1Stream4;
// ...
default:
break;
}
break;
case DmaId::DMA_2:
switch (streamId) {
case DmaStreamId::Stream_1:
return SpiDmaTxStreams::Dma2Stream1;
// ...
default:
break;
}
break;
}
// report compile-time error "invalid DMA-stream combination"
}
答案 0 :(得分:2)
触发constexpr编译错误的一种方法是触发UB。触发UB的最简单方法是通过__builtin_unreachable()
。不幸的是,这不允许消息,但我们可以将它包装在宏中。
作为这个程序的一个例子:
#define CONSTEXPR_FAIL(...) __builtin_unreachable()
constexpr int foo(int a, int b) {
switch (a) {
case 0:
return b;
case 1:
if (b == 2) return 3;
break;
}
CONSTEXPR_FAIL("Mismatch between a and b");
}
int main() {
static_assert(foo(0, 2) == 2, "!");
// constexpr int i = foo(2, 2);
}
使用c ++ 14在gcc 7.2和clang 5.0上编译正常。如果您取消对foo(2,2)
的通话评论,则gcc会发出:
<source>: In function 'int main()':
<source>:18:26: in constexpr expansion of 'foo(2, 2)'
<source>:1:50: error: '__builtin_unreachable()' is not a constant expression
#define CONSTEXPR_FAIL(...) __builtin_unreachable()
~~~~~~~~~~~~~~~~~~~~~^~
<source>:12:5: note: in expansion of macro 'CONSTEXPR_FAIL'
CONSTEXPR_FAIL("Mismatch between a and b");
^~~~~~~~~~~~~~
和clang发出:
<source>:18:19: error: constexpr variable 'i' must be initialized by a constant expression
constexpr int i = foo(2, 2);
^ ~~~~~~~~~
<source>:12:5: note: subexpression not valid in a constant expression
CONSTEXPR_FAIL("Mismatch between a and b");
^
<source>:1:29: note: expanded from macro 'CONSTEXPR_FAIL'
#define CONSTEXPR_FAIL(...) __builtin_unreachable()
^
<source>:18:23: note: in call to 'foo(2, 2)'
constexpr int i = foo(2, 2);
^
这对你有用吗?它不是static_assert
,因为编译器不会直接为您发出消息,但它确实让编译器指向正确的行,并且消息将在调用堆栈中。
答案 1 :(得分:-1)
很抱歉,因为你提出了一个完全不同的解决方案,但是如果
dmaId
和streamId
是文字或constexpr
(枚举类成员),整个函数只能在编译时工作
传递dmaId
和streamId
作为not-template参数在我看来是错误的。
在我看来,更简单的事情如下(抱歉:代码没有经过测试)
// generic foo: to force a comprehensible error message
template <DmaId I1, DmaStreamId I2>
struct foo
{
static_assert( (I1 == DmaId::DMA_1) && (I2 == DmaStreamId::Stream_4),
"your error message here" );
};
// specialization with all acceptable combinations
template <>
struct foo<DmaId::DMA_1, DmaStreamId::Stream_4>
{ static constexpr auto value = SpiDmaTxStreams::Dma1Stream4; };
// ...
template <>
struct foo<DmaId::DMA_2, DmaStreamId::Stream_1>
{ static constexpr auto value = SpiDmaTxStreams::Dma2Stream1; };
// ...
所以,而不是
constexpr value id = spiDmaTxStream(DmaId::DMA_2, DmaStreamId::Stream_1);
你可以写
constexpr value id = foo<DmaId::DMA_2, DmaStreamId::Stream_1>::value;
答案 2 :(得分:-1)
如果你可以在SpiDmaTxStreams
enum中添加一个特殊的错误值...说SpiDmaTxStreams::ErrorValue
...我提出了另一个解决方案,再次基于模板结构但具有恢复的逻辑:非专业struct和static_error
messagge的单一专用版本。
我的意思是......如果在不可接受的组合情况下返回SpiDmaTxStreams::ErrorValue
constexpr SpiDmaTxStreams spiDmaTxStream(DmaId dmaId, DmaStreamId streamId) {
switch (dmaId) {
case DmaId::DMA_1:
switch (streamId) {
case DmaStreamId::Stream_4:
return SpiDmaTxStreams::Dma1Stream4;
// ...
default:
return SpiDmaTxStreams::ErrorValue; // <<---- add this
break;
}
case DmaId::DMA_2:
switch (streamId) {
case DmaStreamId::Stream_1:
return SpiDmaTxStreams::Dma2Stream1;
// ...
default:
return SpiDmaTxStreams::ErrorValue; // <<---- add this
break;
}
}
// report compile-time error "invalid DMA-stream combination"
}
您可以调用spiDmaTxStream()
为模板值(警告:代码未测试)赋值,如下所示
template <DmaId I1, DmaStreamId I2,
SpiDmaTxStreams IR = spiDmaTxStream(I1, I2)>
struct foo
{ static constexpr auto value = IR; };
template <DmaId I1, DmaStreamId I2>
struct foo<I1, I2, SpiDmaTxStreams::ErrorValue>
{
// where DmaId::DMA_1/DmaStreamId::Stream_4 is an
// acceptable combination
static_assert( (I1 == DmaId::DMA_1) && (I2 == DmaStreamId::Stream_4),
"your error message here" );
};
再次,而不是
constexpr value id = spiDmaTxStream(DmaId::DMA_2, DmaStreamId::Stream_1);
你可以写
constexpr value id = foo<DmaId::DMA_2, DmaStreamId::Stream_1>::value;
如果dmaId
/ streamId
不可接受,则spiDmaTxStream()
会返回SpiDmaTxStreams::ErrorValue
,因此会激活foo
专用版本并发送static_error()
消息是负责人。