Data Validation in C++: Overview of Libraries and Practical Approaches
Data validation is one of the key aspects of developing reliable software in C++. Correct input data checking helps prevent runtime errors, vulnerabilities, and undefined behavior. Unlike dynamically typed languages, C++ requires explicit control over types and formats from the developer. In this article, we will look at both manual methods and specialized libraries that simplify validation.
1. Manual Validation: Basic Techniques
The simplest way is to check data using conditional statements. This approach is good for small projects but quickly becomes cumbersome.
#include <iostream>#include <string>#include <cctype>
bool validateEmail(const std::string& email) { if (email.empty()) return false; size_t atPos = email.find('@'); if (atPos == std::string::npos || atPos == 0) return false; size_t dotPos = email.rfind('.'); if (dotPos == std::string::npos || dotPos <= atPos + 1) return false; if (dotPos == email.length() - 1) return false; return true;}
int main() { std::string testEmail = "user@example.com"; std::cout << (validateEmail(testEmail) ? "Valid" : "Invalid") << std::endl; return 0;}Disadvantages: maintenance complexity, code duplication, lack of a unified interface.
2. The libvalidate Library
libvalidate is a lightweight library for declarative data validation in C++17 and later. It allows you to describe validation rules in a chained style reminiscent of validators in PHP or Python.
Installation
The library is distributed as header-only. Simply copy validate.hpp into your project or include it via CMake:
# Example FetchContentinclude(FetchContent)FetchContent_Declare(libvalidate GIT_REPOSITORY https://github.com/username/libvalidate.git GIT_TAG v1.0.0)FetchContent_MakeAvailable(libvalidate)Usage Example
#include "validate.hpp"#include <iostream>#include <string>
int main() { using namespace validate;
// Number validation (age) int age = 25; auto ageResult = check(age) .min(18, "Age must be at least 18") .max(120, "Age must be no more than 120");
if (!ageResult.valid()) { std::cerr << ageResult.error() << std::endl; }
// String validation (password) std::string password = "P@ssw0rd"; auto passResult = check(password) .minLength(8, "Password must contain at least 8 characters") .matches(R"((?=.*[A-Z])(?=.*[a-z])(?=.*\\d).*)", "Password must contain uppercase, lowercase letters and digits");
if (!passResult.valid()) { std::cerr << passResult.error() << std::endl; }
// Combined object validation struct User { std::string name; int age; std::string email; };
User user{"Alice", 30, "alice@example.com"}; auto userResult = check(user) .field(&User::name, "name") .minLength(2) .maxLength(50) .field(&User::age, "age") .range(18, 100) .field(&User::email, "email") .email(); // built-in email validation
if (!userResult.valid()) { for (const auto& err : userResult.errors()) { std::cerr << err.field << ": " << err.message << std::endl; } }
return 0;}Features:
- Support for built-in rules:
min,max,range,email,regex,notEmpty. - Ability to create