在一个网元中,存在着各种“受管对象”(Managed Object)。每种“受管对象”都有自己的一组配置数据。比如,一个“学生”对象的配置数据定义如下:
struct Student
{
unsigned long code;
char name[9];
unsigned char height;
unsigned short score;
};
在“受管对象”配置修改的过程中,如果出了差错,应该可以将配置“回滚”到之前的值。所以,我们定义了如下模版来保存一个配置项。
template <typename T>
struct ConfigValue
{
ConfigValue(const T& defaultValue)
: changed(false)
, currentValue(defaultValue)
, oldValue(defaultValue)
{}
void set(const T& value)
{
if(currentValue == value) return;
oldValue = currentValue;
currentValue = value;
changed = true;
}
const T& get() const
{ return currentValue; }
const T& getOldValue() const
{ return changed ? oldValue : currentValue; }
void resetStatus()
{ changed = false; }
void revert()
{
if(not changed) return;
currentValue = oldValue;
changed = false;
}
bool isChanged() const
{ return changed; }
private:
bool changed;
T currentValue;
T oldValue;
};
基于此模版,我们将“学生”的配置定义为:
struct StudentConfig
{
ConfigValue<unsigned long> code;
ConfigValue<FixedSizeString<8> > name;
ConfigValue<unsigned char> height;
ConfigValue<unsigned short> score;
};
通过SNMP的客户端,用户可以一次性修改某个“受管对象”的多个配置数据。比如,对于某个“学生”对象,用户可以同时修改他的“身高”和“总成绩”。所以我们定义了如下模版来保存由SNMP客户端而来的某个配置项:
template <typename T>
struct OptionalConfigValue
{
OptionalConfigValue() : specified(false) {}
void set(const T& value)
{
this->value = value;
specified = true;
}
void updateTo(ConfigValue<T>& config) const
{
if(specified) config.set(value);
}
private:
bool specified;
T value;
};
以“学生”对象为例,相应的SNMP配置结构则定义为:
struct StudentOptionalConfig
{
OptionalConfigValue<unsigned long> code;
OptionalConfigValue<FixedSizeString<8> > name;
OptionalConfigValue<unsigned char> height;
OptionalConfigValue<unsigned short> score;
};
这样,StudentConfig应该提供一个update接口,来更新来自于SNMP客户端的配置数据。比如:
struct StudentConfig
{
StudentConfig()
: code(0)
, name("anon")
, height(170)
, score(0)
{}
void update(const StudentOptionalConfig& config)
{
config.code.updateTo(code);
config.name.updateTo(name);
config.height.updateTo(height);
config.score.updateTo(score);
}
void revert()
{
code.revert();
name.revert();
height.revert();
score.revert();
}
void configDone()
{
code.resetStatus();
name.resetStatus();
height.resetStatus();
score.resetStatus();
}
private:
ConfigValue<unsigned long> code;
ConfigValue<FixedSizeString<8> > name;
ConfigValue<unsigned char> height;
ConfigValue<unsigned short> score;
};
从上述的实现中,我们能够发现强烈的重复模式:首先,所有的配置信息在不同的地方重复出现,其次,对于不同的配置信息进行相同的处理。
为了解决这样的重复问题,我们首先使用宏来定义“元数据”:
// Student.h
DEFINE_CONFIG (code, unsigned long, 0)
DEFINE_CONFIG (height, unsigned char, 170)
DEFINE_CONFIG (score, unsigned short, 0)
DEFINE_STR_CONFIG(name, 8, "anon")
而“魔法”的关键在于,如何来通过不同的宏定义来“解释”这些元数据。
比如,对于StudentOptionalConfig的定义,我们可以将其代码转化为:
// StudentOptionalConfig.h
struct StudentOptionalConfig
{
#include <OptionalConfigDef.h>
#include "Student.h"
};
而OptionalConfig.h的定义为:
// OptionalConfigDef.h
#ifdef DEFINE_CONFIG
#undef DEFINE_CONFIG
#endif
#define DEFINE_CONFIG(name, type, defaultValue) \
OptionalConfigValue<type> name;
#ifdef DEFINE_STR_CONFIG
#undef DEFINE_STR_CONFIG
#endif
#define DEFINE_STR_CONFIG(name, size, defaultValue) \
OptionalConfigValue<FixedSizeString<size> > name;
类似的,StudentConfig的定义则变为:
// StudentConfig.h
struct StudentConfig
{
StudentConfig() :
#include <DefaultValueDef.h>
#include "Student.h"
, dummy(true)
{}
void update(const StudentOptionalConfig& config)
{
#include <UpdateDef.h>
#include "Student.h"
}
void revert()
{
#include <RevertDef.h>
#include "Student.h"
}
void configDone()
{
#include <ConfigDoneDef.h>
#include "Student.h"
}
private:
#include <ConfigDef.h>
#include "Student.h"
bool dummy;
};
而各个魔法头文件的定义分别为:
// DefaultValueDef.h
#ifdef DEFINE_CONFIG
#undef DEFINE_CONFIG
#endif
#define DEFINE_CONFIG(name, type, defaultValue) \
name(defaultValue),
#ifdef DEFINE_STR_CONFIG
#undef DEFINE_STR_CONFIG
#endif
#define DEFINE_STR_CONFIG(name, size, defaultValue) \
name(defaultValue),
// UpdateDef.h
#ifdef DEFINE_CONFIG
#undef DEFINE_CONFIG
#endif
#define DEFINE_CONFIG(name, type, defaultValue) \
config.name.updateTo(name);
#ifdef DEFINE_STR_CONFIG
#undef DEFINE_STR_CONFIG
#endif
#define DEFINE_STR_CONFIG(name, size, defaultValue) \
config.name.updateTo(name);
// CofigDoneDef.h
#ifdef DEFINE_CONFIG
#undef DEFINE_CONFIG
#endif
#define DEFINE_CONFIG(name, type, defaultValue) \
name.resetStatus();
#ifdef DEFINE_STR_CONFIG
#undef DEFINE_STR_CONFIG
#endif
#define DEFINE_STR_CONFIG(name, size, defaultValue) \
name.resetStatus();
// RevertDef.h
#ifdef DEFINE_CONFIG
#undef DEFINE_CONFIG
#endif
#define DEFINE_CONFIG(name, type, defaultValue) \
name.revert();
#ifdef DEFINE_STR_CONFIG
#undef DEFINE_STR_CONFIG
#endif
#define DEFINE_STR_CONFIG(name, size, defaultValue) \
name.revert();
// ConfigDef.h
#ifdef DEFINE_CONFIG
#undef DEFINE_CONFIG
#endif
#define DEFINE_CONFIG(name, type, defaultValue) \
ConfigValue<type> name;
#ifdef DEFINE_STR_CONFIG
#undef DEFINE_STR_CONFIG
#endif
#define DEFINE_STR_CONFIG(name, size, defaultValue) \
ConfigValue<FixedSizeString<size> > name;
有了这些魔法定义文件,每个配置定义文件就呈现除了简单一致的规则,很容易就可以写出一个代码生成器,自动生成相关的代码。下面是一个Python的实现:
import sys
optional_config_template = '''
#ifndef __%s_OPTIONAL_CONFIG_H__
#define __%s_OPTIONAL_CONFIG_H__
struct %sOptionalConfig
{
#include <OptionalConfigDef.h>
#include "%s.h"
};
#endif
'''
config_template = '''
#ifndef __%s_CONFIG_H__
#define __%s_CONFIG_H__
struct %sConfig
{
%sConfig()
#include <DefaultValueDef.h>
#include "%s.h"
dummy(true)
{}
void update(const %sOptionalConfig& config)
{
#include <UpdateDef.h>
#include "%s.h"
}
void configDone()
{
#include <ConfigDoneDef.h>
#include "%s.h"
}
void revert()
{
#include <RevertDef.h>
#include "%s.h"
}
private:
#include <ConfigDef.h>
#include "%s.h"
bool dummy;
};
#endif
'''
def write_file(name, suffix, content) :
file = open(name + suffix + ".h", "w")
file.write(content)
file.close()
def generate_optional_config(name) :
write_file(name, "OptionalConfig", \
optional_config_template % \
( name.upper(), name.upper(), name, name))
def generate_config(name) :
write_file(name, "Config", \
config_template % \
( name.upper(), name.upper() \
, name, name, name, name, name, name, name, name))
def generate(name) :
generate_optional_config(name)
generate_config(name)
def usage() :
print "usage: python config-gen.py config-name"
if __name__ == "__main__":
if len(sys.argv) != 2:
usage()
sys.exit(1)
generate(sys.argv[1])