想写一个方便无比的配置保存类,但 ms 实现不了?求集思广益!

2016-06-18 18:54:35 +08:00
 rtyurtyu
我先大致描述这个类是什么样子的:
class SaveClass
{
private:
//各种私有方法和变量
//...

public:
//公有方法
read(char * filename);//从 filename 读入配置
save(char * filename);//写入配置到 filename

//公有成员变量
int setting1;//配置数据 1
double setting2;//配置数据 2
std::string setting3;//配置数据 3

}
SaveClass cSave;

setting1 、 setting2 、 setting3 就是配置数据
直接在类定义里添加
这样可以方便的
cSave.setting1=123;
cSave.setting2=0.456;
cSave.setting3="abc 啊啊啊";
来读取和设置

关键点来了!
当 cSave.save("C:\sav.ini")的时候,要能自动写入 sav.ini 如下内容:
setting1=123;
setting2=0.456;
setting3=abc 啊啊啊;
当 cSave.read("C:\sav.ini")的时候,要能自动读入配置文件到对应变量

关键就是自动枚举类中的成员变量,自动把变量名转字符串,自动判断变量名的类型
也就是说我添加一个新成员变量 float setting4 的时候,不需要改写.save 方法和.init 方法就能自动适应

如果用 javascript 来做的话用 eval 、 tostring 等等可以很轻易的实现
但是 c++没有这么方便的函数
算上宏算上模板能不能想办法来用 c++实现设想的这个功能?
2482 次点击
所在节点    C
16 条回复
h4x3rotab
2016-06-18 19:34:40 +08:00
搜索关键词: c++ 序列化
rogerchen
2016-06-18 19:49:02 +08:00
protocol buffer
skydiver
2016-06-18 19:54:44 +08:00
用宏实现
rtyurtyu
2016-06-18 20:08:01 +08:00
我需要文本式的序列化而不能是看不懂的二进制形式的,因为有双击.ini 直接编辑配置文件的需求
如果没有直接编辑的需求那最简单的直接二进制写一个 struct 到文件就搞定了
看了看现有的库好像都不太合适的样子
还是想完全自写
sfqtsh
2016-06-18 20:12:28 +08:00
#include <iostream>
using namespace std;

#define DefineVar(type, name, value) #type" "#name" = ";type name = value
int main()
{
char str[] = DefineVar(int, iAbc, 8);
cout << str << iAbc << endl; //"int iAbc = 8"

return 0;
}
sfqtsh
2016-06-18 20:18:58 +08:00
为什么个人主页里找不到自己在 C/C++/Obj-C 节点里的回复?好几次了 @Livid
k9982874
2016-06-18 20:20:02 +08:00
boost property_tree 操作 ini
tinyxml 操作 xml
jsoncpp 操作 json

没必要自己再造轮子
Sorrow
2016-06-18 20:26:55 +08:00
使用 boost::any 可以存放任意类型的数据,或者直接使用 c++ 17 的 std::any
rtyurtyu
2016-06-18 20:39:47 +08:00
@skydiver
@sfqtsh
想了想好像用宏是可以实现的,虽然要写的很复杂
就在类定义里直接一个宏把 save 、 read 和变量定义都改写出来

@k9982874
还是希望最精简的语法 cSave.xxx=n 就能直接读写配置,重载了=甚至可以省略.save 实时保存
你这些类都会让语法更复杂

@Sorrow
不想用 boost 实在是太臃肿了
Sorrow
2016-06-18 20:52:00 +08:00
@rtyurtyu std::any 已经在最近版本的 c++ 标准库里了, 只要包含相应头文件就行了。
matthewgao
2016-06-19 00:31:26 +08:00
@Sorrow any 好像也不解决他的问题,他是要一个类似 Java 反射的功能
matthewgao
2016-06-19 00:37:33 +08:00
可不可以这样 workaround 一下,用 unordered_map<std::string, std::any>来保存你要解析的属性,就不要把它变成真正的类属性
owt5008137
2016-06-19 00:42:07 +08:00
C++目前还不支持反射,是做不到像 Java 或者 C#那样枚举成员变量的。用宏只是一点点取巧而已,也不是特别方便,可以参考 msgpack-c 的设计,就是宏实现的。

用 protobuf 之类的可能是简单得多的方案了,特别是 protobuf 3 可以直接读写 json ,用它的反射接口写序列化和反序列化也不困难。但是还是得写 proto 文件,并且 protobuf 也是蛮臃肿的。

或者寻求简洁的话可以参考下我的这个 ini 读取库,只有两个文件。 https://github.com/owt5008137/libiniloader
用 std::string 来存数据,用模板来自动检测类型,转换数据内容
nozama
2016-06-19 02:48:45 +08:00
我猜最多只能用 宏、模板鸡肋地实现,或者代码生成器(比如 protobuf....好像太重量级)。

这我想起 rust 的好了,编译器扩展也可以是 library 的一部分,有不少序列化库都利用了这一点,借助编译时的信息来生成代码,可以非侵入式地实现序列化 /反序列化。

我更想说的是,用合适的工具做合适的事、合理地分离关注点,比对语言进行晦涩的 hack 要好。
rtyurtyu
2016-06-19 06:07:37 +08:00
看了 ls 各位的意见又想了很多,突然感觉追求个最精简语法.xxx=x 没有什么意义,非要用静态语言来搞动态语言的事纯粹是找虐

如果用宏来实现,变量定义那里语法仍然怪怪的,宏也复杂得让人看不懂
而且每次新加一个变量都需要改源码重新编译

不如就用 unordered_map<std::string, std::any> cSave;
好处是代码易读,而且能够动态添加
cSave["aaa"]=111;
写起来比 cSave.aaa=111 只多三个字符,就是引号有点烦...

当然现在 std::any 的支持度为 0 ,实际实现还得用一些替代方法
jukka
2016-06-19 13:07:11 +08:00
最简单的做法是继承一个 Lua 虚拟机进去,然后这些事情都在 Lua 里完成。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/286706

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX