在最近的工作中发现c++的宏真是一个很有意思的东西,合理的运用宏能让你少写很多的重复代码。
下面说说宏在处理串口命令时的应用。
我想要达成的目的:通过在串口输入类似命令行的语句更改我设置的全局变量。
比如说有一个全局变量bool b。
当我在串口中输入"bOn;"或"bOff;"时,b的值更改为true和false。
全局变量float f;
当在串口输入"f=10;"时,更改f的值为10;
首先来看看最基本的实现。
- bool b = true;
- float f = 0;
- void setup() {
- // put your setup code here, to run once:
- Serial.begin(9600);
- }
-
- void loop() {
- // put your main code here, to run repeatedly:
-
- }
-
- void serialEvent()
- {
- //串口指令以换行符';'结尾
- String s = Serial.readStringUntil(';');
- procCmd(s);
- }
-
- void procCmd(String s)
- {
- if (s.startsWith("bOn"))
- {
- b = true;
- }
- if (s.startsWith("bOff"))
- {
- b = false;
- }
- if (s.startsWith("f="))
- {
- s.remove(2);
- f = s.toFloat();
- }
- }
复制代码
是不是很简单,当时当有几十个变量时又怎么办呢?很多人的第一想法肯定是复制,然后改名,当然这样并没有错,我的第一想法也是这样,但是做了几遍之后我就厌烦了,这种方法一点也不优雅,就是重复性的无聊工作,而且一旦忘记修改哪个变量就会造成编译错误,同时也产生了大片的重复性代码。
解决重复性工作最好的方式就是抽象出相同的东西,然后重构。
这里,用两个宏就能描述这个结构:
- #define boolCmd(cmd) do{if(s.startsWith(""#cmd"On")){cmd = true;}if (s.startsWith(""#cmd"Off")){cmd = false;}}while(0)
- #define doubleCmd(cmd) do{String s1 = ""#cmd""; if(s.startsWith(""#cmd"=")){s.remove(0, s1.length()+1);cmd=s.toFloat();}}while(0)
复制代码
这两个宏中"#cmd"将cmd这个字符当成字符串来处理,于是命令中的"bOn"中的"b"可以被我们替换成""#cmd"On",代替任意的变量名,而处理语句中的cmd代表的是这个变量本身,是不是很有意思,相当于在命令中直接引用了变量的名称。
对float的处理也是相同的。重构之后的代码变成了这样:
- #define boolCmd(cmd) do{if(s.startsWith(""#cmd"On")){cmd = true;}if (s.startsWith(""#cmd"Off")){cmd = false;}}while(0)
- #define floatCmd(cmd) do{String s1 = ""#cmd""; if(s.startsWith(""#cmd"=")){s.remove(0, s1.length()+1);cmd=s.toFloat();}}while(0)
- bool b = true;
- float f = 0;
- void setup() {
- // put your setup code here, to run once:
- Serial.begin(9600);
- }
-
- void loop() {
- // put your main code here, to run repeatedly:
-
- }
-
- void serialEvent()
- {
- //串口指令以换行符';'结尾
- String s = Serial.readStringUntil(';');
- procCmd(s);
- }
-
- void procCmd(String s)
- {
- boolCmd(b);
- floatCmd(f);
- }
复制代码
是不是看起来简单多了,而且如果有其他变量需要修改,只需要用这两个宏在串口处理函数中注册就可以了,极大的减少了重复性工作。
同时我还定义了另一个宏:
- #define prt(cmd) do{String s1 = ""#cmd""; if(s.startsWith("?"#cmd"")){Serial.println(cmd);}}while(0)
复制代码
通过输入"?变量名;",就能打印我想查看的变量。
这类的应用还有很多很多,大家可以自行扩展。
最后或许有人会说用字符串处理效率太低,确实也比较占用空间,但我的观点是一个有意义的语句在调试时要直观的多,如果条件允许,这样绝对是值得的。
|