1

オブジェクト指向のアプローチを使用して、関数/メソッドが受け取るパラメーターのセットを検証するソリューションを考えていました。たとえば、次のスニペットでは、パラメーターは使用前に「手動で」チェックされます。

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 ...
}  

パラメータの数が増えると、検証コードのサイズも大きくなります。パラメータ(文字列とポインタ)が空かnullかをチェックする基本的なアプローチを考えました(目的は、機能を提供するコードをより読みやすくすることです)。

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 は、最初の配列の各要素が空か null かをチェックし、quiet フラグが設定されていません。
私の実装では、これは次のようになります。

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

メッセージは別のファイルに配置することもできるため、メソッド本体がよりきれいになります。
数値範囲チェッカーも同様に実装できます。ただし、このアプローチでは、パラメーター間の依存関係は考慮されません。

おそらくテンプレートを使用して、パラメーター検証メカニズムを実装するより洗練されたソリューションはありますか?

4

2 に答える 2

4

より洗練された方法は、パラメーターに標準型を使用するのではなく、構築時にパラメーターをチェックする特定のクラスを定義することです。何かのようなもの

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これにより、 anの有効性が構築時および場合によっては変更時にのみチェックされるようにすることも容易になります。

これらのスライドは、2007 年の ACCU カンファレンスでの Ric Parkin によるプレゼンテーションからのもので、このアイデアをより詳細に検討しています。

于 2013-08-22T19:46:50.133 に答える
1

おそらく、関数名のオーバーロードと可変個引数テンプレートを利用する方が簡単だとわかるでしょう。検証するパラメーター情報と修正アクションを .xml ファイルにまとめてグループ化できます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"; })
               //...
              );
于 2013-08-22T19:12:56.930 に答える