2013-08-22 46 views
1

我一直在想一个解决方案来验证函数/ mehod使用面向对象方法接收的参数集。例如,在下面的代码片段中,参数在使用前会“手动”检查。Paramater验证C++

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); 
} 
} 

消息还可以被放置在一个单独的文件从而使得该方法体清洁器。
数值范围检查器也可以类似地实现。但是,这种方法不考虑参数之间的依赖关系。

是否有更优雅的解决方案来实现参数验证机制,可能使用模板?

+1

认真地努力看看后一种方法比前者更容易,特别是在可读性方面。 – WhozCraig

+0

'#ifndef NDEBUG'是一个不行,剩下的就是设计复杂的方式 –

+0

我会说前者更具可读性。作为一种惯例,您应该了解您的功能在读取时的功能。当我读到第二个实现的'InstallData :: InstallData'时,我的脑海里想着什么是这个矢量 - 不认为这个设计有帮助。 –

回答

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探索更详细的想法。

+0

这是一个很好的解决方案,但缺点是它需要在添加新参数时实施新类。但是,我猜想,提问者希望每个参数都有不同的行为,但这可能不是坏事。 – jxh

+0

这是一件好事!让我们利用类型系统! –

+0

可以设想一个NonEmptyString基类来限制重复。 –

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"; }) 
       //... 
      ); 
+0

+1模板解决方案 – Sebi