1

I've been thinking of a solution to validate the set of parameters a function/method receives using an object oriented approach. For example, in the following snippet the parameters are checked "manually" before being used.

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

As the number of parameters increases so does the size of the validation code. I've thought of a basic approach of checking parameters(strings and pointers) as whether they are either empty or null(the aim is to make the code providing functionality more readable).

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

The validation methods validateStrs and validatePtrs check whether each element of the first array is empty or null and display a message from the second array(there is a one to one relationship between the elements of the first array and the second) if the quiet flag is not set.
In my implementation this looks like:

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

The messages can also be placed in a separate file thus making the method body cleaner.
Numeric value range checkers can also be implemented similarly. This approach, however, doesn't consider dependencies between parameters.

Is there a more elegant solution of implementing a parameter validation mechanism, possibly using templates?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Sebi
  • 4,262
  • 13
  • 60
  • 116
  • 1
    Seriously struggling to see how the latter approach is "easier" than the former, especially in terms of readability. – WhozCraig Aug 22 '13 at 18:45
  • '#ifndef NDEBUG' is a no go here and the rest is way to complicate in design –  Aug 22 '13 at 18:47
  • Former is more readable code I'll say. As a general practice you should get an idea of what your function does when you read it. As I read the second implementation's `InstallData::InstallData` my mind runs into what is this vector for - don't think this design helps. – Manoj Awasthi Aug 22 '13 at 18:53
  • For a few number of parameters it's not easier but it pays off if dealing with a larger number of parameters(6-7) – Sebi Aug 22 '13 at 18:53
  • @Sebi If you have that many parameters for a function you should consider placing them in a [POD](http://stackoverflow.com/questions/146452/what-are-pod-types-in-c) and do the validation there (e.g. on initialization). Usually you shouldn't have this big number of orthogonal parameters in a good design. – πάντα ῥεῖ Aug 22 '13 at 19:08
  • @ g-makulik - this question is part of a larger context. I am looking for a design that can cope with multiple parameter validation and if possible also handle different levels of verbosity. – Sebi Aug 22 '13 at 19:24
  • If the parameter isn't correct, don't apply a default just error. That will save some lines. Do you want to validate programmer errors or user errors? If the former I would just use assert. The debugger will tell you what happened. – Neil Kirk Aug 22 '13 at 22:26
  • @ Neil Kirk It's about programmer errors. Assert may be helpful in debug mode but errors can crop up in release builds. I'd rather use "classic" validation. – Sebi Aug 22 '13 at 22:38

2 Answers2

4

A more elegant way is not to use standard types for parameters but to define specific classes that check parameters on construction. Something like

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

This would also make it easier to ensure that an AppPath is checked for validity only on construction and possibly on modification.

These slides from a presentation by Ric Parkin at the 2007 ACCU Conference explore the idea in greater detail.

Nicola Musatti
  • 17,834
  • 2
  • 46
  • 55
  • This is a nice solution, but the downside is that it requires a new class to be implemented when a new parameter is added. But, I guess given that the asker wants a different behavior for each parameter, it may not be that bad of a downside. – jxh Aug 22 '13 at 19:51
  • One could envision a NonEmptyString base class to limit repetition. – Nicola Musatti Aug 22 '13 at 19:56
  • I agree, the downside is not that significant. +1. – jxh Aug 22 '13 at 19:58
  • +1 I like the idea behind classes that check parameters upon construction. Also, as pointed out by jxh, this may end up in a swarm of classes. – Sebi Aug 22 '13 at 22:10
  • One way to limit the problem of having very many classes is to limit their visibility as much as possible. – Nicola Musatti Aug 22 '13 at 22:16
1

Perhaps you would find it easier to leverage function name overloading and variadic templates. You can group the parameter information you want to validate along with the corrective action together in a std::tuple. I implemented a small demo of this idea on 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...);
}

Then, you could use it like:

bool result
    = validate(
          std::make_tuple(appPath, "ApplicationPath",
                          [&](){ appPath = "defaultPath"; }),
          std::make_tuple(appName, "ApplicationName",
                          [&](){ appName = "defaultName"; })
               //...
              );
jxh
  • 69,070
  • 8
  • 110
  • 193