【发布时间】:2020-04-24 20:48:02
【问题描述】:
我有带有递减软件计数器的 C 程序。例如,如果我想每 2 秒闪烁一次 LED,我可以这样做:
if(!ledT) {
ledT = 200;
// code
// code
// code
}
因为我总是对每个计数器进行完全相同的组合,所以我倾向于输入一行。
if(!ledT) { ledT = 200;
// code
// code
// code
}
对于整个 if 行,我想改用宏。所以代码看起来像:
expired(ledT, 200) {
// code
// code
// code
}
我在进入状态的状态机代码中使用了类似的东西。
if(runOnce) { runOnce = false;
// code
// code
// code
所需的语法:
entryState {
// code
// code
// code
.
#define entryState if(runOnce) { runOnce = false; // this ofcourse cannot work But something like this is what I want.
我已经做了几次尝试,但都没有成功。问题是{ 位于宏的中间位置,我想在宏后面键入{,因为众所周知,没有代码编辑器可以处理不相等数量的{ 和@987654331 @。
expired(ledT, 200); // expired is macro, not function
// code
// code
// code
}
所以这是不可能的。
在阅读宏时,我读到了一些关于使用的有趣内容:do ... while(0)。这种“技巧”滥用编译器的优化功能来创建某个宏,否则这是不可能的。
阐明了这种方式。
有没有办法使用某种“宏观技巧”来实现我想要的?
再说一遍,这就是转变:
// this
if(runOnce) {
runOnce = false;
// code
// code
// code
// into this
entryState {
// code
// code
// code
// and this:
if(!someTimer) {
someTimer = someInterval;
// code
// code
// code
// must be transformed into:
timeExpired(someTimer, someInterval) {
// code
// code
// code
类似“不,根本做不到”的回答也将被接受(前提是您知道自己在说什么)
编辑: 我需要添加一个补充,因为似乎不是每个人都知道我想要什么,最后给出的答案甚至不是针对手头的具体问题。不知何故切换 IO 突然变得很重要?因此我修改了我的代码示例以更好地说明问题所在。
编辑2: 我同意 timeExpired 宏根本不会提高可读性
为了说明某些宏可以提高可读性,我将给出一个状态和状态机的 sn-p。这是我的代码中生成的状态的样子:
State(stateName) {
entryState {
// one time only stuff
}
onState {
// continous stuff
exitFlag = true; // setting this, exits the state
}
exitState {
// one time only stuff upon exit
return true;
}
}
目前使用这些宏:
#define State(x) static bool x##F(void)
#define entryState if(runOnce)
#define onState runOnce = false;
#define exitState if(!exitFlag) return false; else
我想我应该在美国用EXIT 或其他更漂亮的东西交换return true;。
调用这些状态的状态机看起来像:
#undef State
#define State(x) break; case x: if(x##F())
extern bit weatherStates(void) {
if(enabled) switch(state){
default: case weatherStatesIDLE: return true;
State(morning) {
if(random(0,1)) nextState(afternoon, 0);
else nextState(rain, 0); }
State(afternoon) {
nextState(evening, 0); }
State(evening) {
if(random(0,1)) nextState(night, 0);
else nextState(thunder, 0); }
State(night) {
nextState(morning, 0); }
State(rain) {
nextState(evening, 0); }
State(thunder) {
nextState(morning, 0); }
break; }
else if(!weatherStatesT) enabled = true;
return false; }
#undef State
唯一没有生成的是 'nextState()' 函数之前的 'if' 和 'else'。这些“流动条件”需要填写。
如果向用户提供了一个小例子或解释,他应该完全没有困难填写状态。他还应该能够手动添加状态。
我什至想用宏来交换这个:
extern bit weatherStates(void) {
if(enabled) switch(state){
default: case weatherStatesIDLE: return true;
和
break;} }
else if(!weatherStatesT) enabled = true;
return false;}
我为什么要这样做?将不相关的信息隐藏在您的显示器之外。删除状态机中的许多选项卡。通过使用简单的语法来提高整体可读性。与第 3 个库函数一样,您需要知道如何使用代码,而不是知道函数是如何发挥作用的。
您不需要知道状态是如何发出准备好的信号的。知道所讨论的函数被用作状态函数比知道它返回一个位变量更重要。
我还在使用前测试宏。所以我不会向别人提供可能表现出奇怪行为的状态机。
【问题讨论】:
-
为什么不使用函数而不是宏?
-
为什么要使用宏?第一个代码示例清晰明了。所有其他的只是模糊了代码(它们看起来不像是有效的 C 代码),你不会从需要维护它的任何其他人那里得到任何感谢。如果你真的想使用一个函数。
-
Because I always do the exact same combination with every counter, I tend to type it one line.这只是普通丑陋的代码。 -
Swedgin -> 将代码放在一行中很难看,这就是我想要一个宏的原因。 Konrad -> 使用函数通常比较慢,尤其是在传递争论的情况下。 Kaylum -> 在这种特殊情况下可能是这样,但宏可以增加可读性。如果你下载一些随机库来让一些 I2C 传感器工作,你只需要寻找示例代码就可以“完成工作”。根本不需要在库本身内部进行挖掘。宏也是如此。代码可读性取决于使用的语法。
-
@basknippels
Because I always do the exact same combination with every counter这需要一个函数。就像在教科书示例中重构为子函数一样。