向阳花木(二)C++ ATTR 宏自动属性生成器——封装配置项
ATTR优点:大幅减少样板代码、风格统一、使用简单、支持移动语义。如果每个配置项都手写成员变量 + Getter + Setter,会产生大量重复代码,可维护性较差。因此,项目中引入了一个 `ATTR 宏`,用来自动生成“属性接口”,简化开发工作。本文结合实际项目代码,总结 ATTR 的设计原理、读写方式及正确使用方法。
·
C++ 宏封装配置项(多属性接口)
1. 背景
在实际项目中,我们经常需要维护大量配置项,例如:
- 串口地址
- 网络地址
- 标定参数
- 传感器参数等
如果每个配置项都手写成员变量 + Getter + Setter,会产生大量重复代码,可维护性较差。因此,项目中引入了一个 ATTR 宏,用来自动生成“属性接口”,简化开发工作。本文结合实际项目代码,总结 ATTR 的设计原理、读写方式及正确使用方法。
2. ATTR 宏的定义
ATTR生成的是“函数属性”,不是变量
- 读:name()
- 写:name(value)
ATTR优点
- 大幅减少样板代码
- 风格统一
- 使用简单
- 支持移动语义
ATTR缺点
- 依赖宏,不易调试
- IDE 跳转不友好
- 扩展逻辑困难
- 报错信息不直观
适合内部工具类、配置类使用。
三个关键点
- 所有访问必须走函数
- 赋值只能在函数中完成
- 默认值 → ini 覆盖 → 运行使用 → 保存回写
2.1 ATTR.h
#ifndef ATTR_H
#define ATTR_H
/*
* 使用前提:
* - ATTR 必须写在 class 的 private: 区域
* - 成员变量属于 private
* - Getter / Setter 自动提升为 public
*/
#define ATTR(Type, Name, Default) \
Type _##Name = Default; \
\
public: \
/* Getter */ \
const Type& Name() const noexcept { return _##Name; } \
\
/* Setter(lvalue) */ \
void Name(const Type& value) noexcept { _##Name = value; } \
\
/* Setter(rvalue) */ \
void Name(Type&& value) noexcept { _##Name = std::move(value); } \
private:
#endif // ATTR_H
2.2 ATTR 实际生成的代码
例如:
ATTR(std::string, sensorSeialPort, "COM3");
等价于:
private:
std::string _sensorSeialPort = "COM3";
public:
// Getter
const std::string& sensorSeialPort() const {
return _sensorSeialPort;
}
// Setter(拷贝)
void sensorSeialPort(const std::string& v) {
_sensorSeialPort = v;
}
// Setter(移动)
void sensorSeialPort(std::string&& v) {
_sensorSeialPort = std::move(v);
}
private:
可以看到:
- 生成了一个私有成员变量 _sensorSeialPort
- 生成了一个 Getter
- 生成了两个 Setter
ATTR 本质上就是一个“自动属性生成器”。
2.3 最小示例
class Test {
private:
ATTR(int, age, 10)
};
int main() {
Test t;
// 读
std::cout << t.age() << std::endl; // 10
// 写
t.age(20);
std::cout << t.age() << std::endl; // 20
}
3. ATTR 使用示例
定义一个ConfigIni.hpp
class ConfigIni {
private:
ATTR(int, age, 10);
ATTR(double, upperAxisLimit,100.f);
ATTR(double, downAxisLimit,100.f);
ATTR(std::string, sensorSeialPort,"COM3"); // 默认初始化
void ReadData() {
sensorSeialPort(ini.GetValue("Sensor", "SeialPort", "COM1"));
}
void SaveData() {
ini.SetValue("Sensor", "SeialPort", sensorSeialPort().c_str());
}
};
3.1 ATTR 的基本使用规则
-
必须写在 private 区域
ATTR结尾自带 private:,因此必须放在类的 private 区域中。
正确示例:class Config { private: ATTR(int, age, 10) }; -
ATTR不是变量,是函数接口。使用 ATTR 后:- 不能直接访问成员变量
- 必须通过函数访问
这是理解 ATTR 的关键。
3.2 读取属性(Getter)
基本语法
name();
示例:
std::string addr = sensorSeialPort();
double limit = upperAxisLimit();
3.3 属性赋值(Setter)
基本语法
name(value);
像调用函数一样赋值。
-
字面量赋值
sensorSeialPort("COM5"); -
变量赋值
std::string s = "COM8"; sensorSeialPort(s);
3.4 常见错误总结
- 当普通变量使用
❌ 错误:powerAddress = "COM3";
✅ 正确:powerAddress("COM3"); - 忘记加括号
❌ 错误:powerAddress;
✅ 正确:powerAddress(); - 试图通过返回值修改
❌ 错误:powerAddress() = "COM3";
Getter 返回 const,禁止修改。 - 在类体中直接赋值
❌ 错误:powerAddress("COM1"); // 写在 class 里
只能写在函数(构造函数 / 成员函数)中。
更多推荐



所有评论(0)