我有一些全局变量,一旦读取配置文件就会赋值。
bool bar1;
int bar2;
string bar3;
我读了下面的配置文件:
foo1 = 12
foo2 = 0
foo3 = 1
...
void func()
{
//read file into a std::map mp
for(auto i:mp)
{
if(i.first=="foo1")
bar1 = i.second;
else if(i.first=="foo2")
bar2 = i.second;
else if(i.first=="foo3")
bar3 = i.second;
.....
}
}
我有很多这样的变量要从文件初始化。有没有更好的方法来做到这一点,因为这会使我的功能膨胀。
PS:我仍然坚持使用C ++ 03。答案 0 :(得分:0)
在我的评论中,我详细阐述了Jabberwocky使用std::map
的想法。
实际上,我们在S / W中做了类似的配置和类似的事情。唯一的区别 - 我们不会使用std::map
作为预定义数组。 (我不喜欢在运行时必须完成某些事情的想法,这在编译之后实际上永远不会改变。)为了演示这个概念,我做了一点MCVE:
#include <iostream>
#include <cassert>
#include <cstring>
#include <algorithm>
#include <map>
int main()
{
// variables
int bar1 = 0, bar2 = 0, bar3 = 0;
// symbol table
const struct Entry { const char *key; int *pVar; } table[] = {
{ "foo1", &bar1 },
{ "foo2", &bar2 },
{ "foo3", &bar3 }
};
const size_t nTable = sizeof table / sizeof *table;
// check that table has correct order
assert([&]()
{
for (size_t i = 1; i < nTable; ++i) {
if (strcmp(table[i - 1].key, table[i].key) >= 0) return false;
}
return true;
}());
// use table in tests
std::pair<const char*, int> mp[] = {
{ "foo1", 123 },
{ "foo2", 234 },
{ "foo3", 345 },
{ "wrong", 666 }
};
// evaluate mp of OP
for (auto i : mp) {
const Entry e = { i.first, 0 };
const auto iter
= std::lower_bound(std::begin(table), std::end(table), e,
[](const Entry &e1, const Entry &e2) { return strcmp(e1.key, e2.key) < 0; });
if (iter != std::end(table) && strcmp(iter->key, i.first) == 0) *iter->pVar = i.second;
else std::cerr << "Unknown var '" << i.first << "'!\n";
}
// print result
std::cout
<< "bar1: " << bar1 << '\n'
<< "bar2: " << bar2 << '\n'
<< "bar3: " << bar3 << '\n';
// done
return 0;
}
输出:
Unknown var 'wrong'!
bar1: 123
bar2: 234
bar3: 345
基本部分是struct Entry
,它将选项的名称与相应变量的地址分组。这可用于在std::map
中存储名称和变量地址对。
我使用的是预先排序的数组。 (在编程中手动对键进行排序并不困难 - 如果发生意外,assert()
会发出警报。)
在我们高效的S / W中,我们没有使用变量的地址,但是使用方法指针来设置setter函数,因为目标变量具有不同的类型,并且值(作为字符串提供)是resp的主题。解析。但是,这些方法指针是编译时可解决的→整个表可以是static
。因此,防止了为每个函数调用构建表的努力。在此演示中,表将地址存储到局部变量。这让我觉得static
表可能是一个坏主意(我甚至没有尝试过)。
根据请求,这里使用方法指针设置setter方法的另一个演示:
#include <iostream>
#include <cassert>
#include <cstring>
#include <string>
#include <algorithm>
class Object {
private:
// some member variables:
int var1, var2;
std::string var3;
double var4;
public:
Object(): var1(), var2(), var4() { }
friend std::ostream& operator<<(std::ostream &out, const Object &obj);
// the setter methods
void setVar1(const char *value) { var1 = atoi(value); }
void setVar2(const char *value) { var2 = atoi(value); }
void setVar3(const char *value) { var3 = value; }
void setVar4(const char *value) { var4 = strtod(value, nullptr); }
// the config method to set value by text
void config(const char *key, const char *value)
{
// symbol table
static const struct Entry {
const char *key; // the symbol
void (Object::*set)(const char*); // the corresponding setter method
} table[] = {
{ "var1", &Object::setVar1 },
{ "var2", &Object::setVar2 },
{ "var3", &Object::setVar3 },
{ "var4", &Object::setVar4 }
};
enum { nTable = sizeof table / sizeof *table };
// check that table has correct order (paranoid - debug only code)
assert([&]()
{
for (size_t i = 1; i < nTable; ++i) {
if (strcmp(table[i - 1].key, table[i].key) >= 0) return false;
}
return true;
}());
// find setter by key
const Entry e = { key, nullptr };
const auto iter
= std::lower_bound(std::begin(table), std::end(table), e,
[](const Entry &e1, const Entry &e2) { return strcmp(e1.key, e2.key) < 0; });
if (iter != std::end(table) && strcmp(iter->key, key) == 0) {
(this->*iter->set)(value);
} else std::cerr << "Unknown var '" << key << "'!\n";
}
};
std::ostream& operator<<(std::ostream &out, const Object &obj)
{
return out
<< "var1: " << obj.var1 << ", var2: " << obj.var2
<< ", var3: '" << obj.var3 << "', var4: " << obj.var4;
}
int main()
{
Object obj;
// print obj before config:
std::cout << "obj: " << obj << '\n';
// configure obj
std::pair<const char*, const char*> config[] = {
{ "var1", "123" },
{ "var2", "456" },
{ "var3", "text" },
{ "var4", "1.23" },
{ "evil", "666" }
};
for (const auto& entry : config) {
obj.config(entry.first, entry.second);
}
// print obj after config:
std::cout << "obj: " << obj << '\n';
// done
return 0;
}
输出:
obj: var1: 0, var2: 0, var3: '', var4: 0
Unknown var 'evil'!
obj: var1: 123, var2: 456, var3: 'text', var4: 1.23
table
(Object::config()
)中的内容为static const
,将在编译时构建(希望&#34;烧掉&#34;到二进制文件中)。因此,Object::config()
的多次调用只能对匹配的key
进行二进制搜索,并在成功的情况下调用setter。
一个重要的前提条件是所有setter方法都具有相同的签名。否则,将它们存储在数组中将是不可能的,因为它们都必须与数组中的方法指针元素兼容。