项目场景:
在使用webots时, 发现webots里面, 构建的模型
和代码中的控制句柄
是通过模型的名字:name(字符串)建立关系的, 官方的写法如下:
Motor *motor = robot->getMotor("motorname");
问题描述
提示:这里描述项目中遇到的问题:
那么最容易想到的写法当然是
#include <string>
typedef enum
{
MOTO_IDX1 = 0,
MOTO_IDX2,
MOTO_IDX3,
MOTO_IDX4,
MOTO_TTL_NUM
}moto_idx_t;
std::motor_names[MOTO_TTL_NUM] = { "motorname1",
"motorname2",
"motorname3",
"motorname4" };
Motor *motors[MOTO_TTL_NUM];
for(int i = 0; i<MOTO_TTL_NUM ;++i){
motors[i] = robot->getMotor( motor_names[i] );
}
但这个写法感觉还是不够优雅, 因为motor_names
数组得一个个添加, 并且名字格式写死, 并不方便后期的维护与修改.
因为之前有看过以下的写法,
// 第一种宏定义用法
#define LED_ON(x) HAL_GPIO_Write_Pin(LED##x##_Port,LED##x##_Pin,GPIO_PIN_SET);
LED_ON(1);
// 运行结果:
// 执行函数:HAL_GPIO_Write_Pin(LED1_Port,LED1_Pin,GPIO_PIN_SET);
// 第二种宏定义用法
#define hello(x) "hello"#x
printf(hello(1));
// 运行结果:
// 打印出:hello1
那么就想通过宏定义的方法来优雅实现, 所以理所当然地改了:
#include <string>
typedef enum
{
MOTO_IDX1 = 0,
MOTO_IDX2,
MOTO_IDX3,
MOTO_IDX4,
MOTO_TTL_NUM
}moto_idx_t;
#define motor_name(x) "motor_name##x"
Motor *motors[MOTO_TTL_NUM];
for(int i = 0; i<MOTO_TTL_NUM ;++i){
motors[i] = robot->getMotor( motor_name(i+1) );
}
修改后,程序有BUG,webots提示找不到"motor_name##i+1"
于是又换成第二种写法
#include <string>
typedef enum
{
MOTO_IDX1 = 0,
MOTO_IDX2,
MOTO_IDX3,
MOTO_IDX4,
MOTO_TTL_NUM
}moto_idx_t;
#define motor_name(x) "motor_name"#x
Motor *motors[MOTO_TTL_NUM];
for(int i = 0; i<MOTO_TTL_NUM ;++i){
motors[i] = robot->getMotor( motor_name(i+1) );
}
程序出了BUG,webots提示找不到"motor_namei+1"
原因分析:
提示:这里填写问题的分析:
-
写法一分析
这个地方就是因为我从以下写法得到灵感// 第一种宏定义用法 #define LED_ON(x) HAL_GPIO_Write_Pin(LED##x##_Port,LED##x##_Pin,GPIO_PIN_SET); LED_ON(1); // 运行结果 // HAL_GPIO_Write_Pin(LED1_Port,LED1_Pin,GPIO_PIN_SET);
然后自以为字符串可以同样操作, 但实际上, 在宏定义展开时, 在双引号
" "
里面的##
是不会形成链接的, 而是保留下来, 所以调用motor_name(i+1)
之后, 预处理时候只是将x
替换成i+1
, 所以结果是"motor_name##i+1"
, webot肯定找不到这个定义, 所以崩溃. -
写法二分析
众所周知, 宏定义展开发生在预处理阶段, 但有时候一不小心就容易忘记这一点而导致错误, 正如上面的写法二. 在预处理阶段,motor_name(i+1)
会被展开成为"motor_namei+1"
这个字符串, 所以在程序运行阶段, 变量i
已经失去了它该有的价值, 它已经和"motor_namei+1"
这个字符串融为一体, 所以每一个motors[i]
都会指向
"motor_namei+1"
返回的结果, webot肯定找不到这个定义, 所以崩溃.
解决方案:
提示:这里填写该问题的具体解决方案:
由于宏定义是在预处理就完成了所有展开,所以要用宏定义的方法生成由变量来决定后缀的字符串
是行不通的, 所以只能弃用宏定义, 最终实现如下:
#include <string>
typedef enum
{
MOTO_IDX1 = 0,
MOTO_IDX2,
MOTO_IDX3,
MOTO_IDX4,
MOTO_TTL_NUM
}moto_idx_t;
const std::string gen_wheel_tag(int x){
return "motor_name"+ std::to_string(x);
}
Motor *motors[MOTO_TTL_NUM];
for(int i = 0; i<MOTO_TTL_NUM ;++i){
motors[i] = robot->getMotor( gen_wheel_tag(i+1) );
}
由这次经历, 也是体会到了基础知识的重要性, 故此文记之.