我一直在想一个解决方案来验证函数/ mehod使用面向对象方法接收的参数集。例如,在以下代码段中,参数在使用前“手动”检查。
InstallData::InstallData(std::string appPath, std::string appName,
std::string errMsg) {
if(appPath.empty()) {
#ifndef NDEBUG
std::cout << "Path not specified" << std::endl;
#endif
}
if(appName.empty()) {
#ifndef NDEBUG
std::cout << "Application name not specified" << std::endl;
std::cout << "Defaulting to AppName" << std::endl;
this->appName = "AppName";
#endif
}
if(errMsg.empty()) {
#ifndef NDEBUG
std::cout << "Error message not specified" << std::endl;
std::cout << "Defaulting to Error" << std::endl;
this->errMsg = "Error";
#endif
}
// ... further initialization beyond this point ...
}
随着参数数量的增加,验证码的大小也会增加。我想到了一种检查参数(字符串和指针)的基本方法,无论它们是空还是空(目的是使代码提供更具可读性的功能)。
class Validator {
public:
bool validateStrs(std::vector<std::string> strings, std::vector<std::string> messages, bool quiet);
bool validateStr(std::string str, std::string message, bool quiet);
bool validatePtrs(std::vector<void*> ptrs, std::vector<std::string> messages, bool quiet);
bool validatePtr(void* ptr, std::string message, bool quiet);
};
验证方法validateStrs和validatePtrs检查第一个数组的每个元素是空还是空并显示来自第二个数组的消息(第一个数组的元素与第二个数组之间存在一对一的关系)安静的旗帜没有设定 在我的实现中,这看起来像:
InstallData::InstallData(std::string appPath, std::string appName,
std::string errMsg, std::string errTitle) {
// Initialize string container
std::vector<std::string> strings;
strings.push_back(appPath);
strings.push_back(appName);
strings.push_back(errMsg);
strings.push_back(errTitle);
// Initialize name container
std::vector<std::string> names;
names.push_back("ApplicationPath");
names.push_back("ApplicationName");
names.push_back("ErrorMessage");
names.push_back("ErrorTitle");
boost::shared_ptr<Validator> valid(new Validator());
bool result = true;
#ifndef NDEBUG
result = valid->validateStrs(strings, names, false);
#else
result = valid->validateStrs(strings, names, true);
#endif
if(result){
this->appPath = appPath;
this->appName = appName;
this->errMsg = errMsg;
this->errTitle = errTitle;
} else {
std::exit(0);
}
}
消息也可以放在一个单独的文件中,从而使方法体更清洁 数值范围检查器也可以类似地实现。但是,这种方法不考虑参数之间的依赖关系。
是否有更优雅的实现参数验证机制的解决方案,可能使用模板?
答案 0 :(得分:3)
更优雅的方法是不使用参数的标准类型,而是定义检查构造参数的特定类。像
这样的东西class InvalidAppPath {};
class AppPath {
public:
AppPath(const std::string & appPath) : path(appPath) {
if ( appPath.empty() ) throw InvalidAppPath();
}
operator std::string() { return path; }
private:
std::string path;
};
这也可以更容易确保仅在构造和可能的修改时检查AppPath
的有效性。
These slides from a presentation by Ric Parkin at the 2007 ACCU Conference更详细地探讨了这个想法。
答案 1 :(得分:1)
也许您会发现利用函数名称重载和可变参数模板更容易。您可以在std::tuple
中将要验证的参数信息与纠正措施一起分组。我在IDEONE上实现了这个想法的小型演示。
bool validate (std::string s) { return !s.empty(); }
bool validate (const void *p) { return p; }
template <typename Tuple>
bool validate (Tuple param) {
if (validate(std::get<0>(param))) return true;
#ifndef NDEBUG
std::cout << "Invalid: " << std::get<1>(param) << std::endl;
std::get<2>(param)();
#endif
return false;
}
bool validate () { return true; }
template <typename T, typename... Params>
bool validate (T param, Params... params) {
return validate(param) & validate(params...);
}
然后,您可以使用它:
bool result
= validate(
std::make_tuple(appPath, "ApplicationPath",
[&](){ appPath = "defaultPath"; }),
std::make_tuple(appName, "ApplicationName",
[&](){ appName = "defaultName"; })
//...
);