C++ header-only library for generic data validation.

Overview

cpp-validator (C++ Data Validation Library)

Motivation

It is quite a common task to check if data satisfies specific constraints. For example, server backend must check validity of commands that come from the clients. Another example is a requirement to validate input data entered in the user interface and then show messages describing validation errors.

A typical approach for implementing complex data validation is writing nested if-conditions or chained invocations of partial validation methods. Declarations of the constraints could become intermixed with their implementations so that they would spread over the code which makes it hard to maintain them. Requirement to construct and show error messages could make everything still more complicated.

cpp-validator library allows one to declare data constraints with clean statements in certain points of code and apply them in other points of code on demand. If needed, the validation error messages are automatically constructed by taking into account the user's locale.

Minimal example:

// define validator
auto v1=validator(
        _[key1][key1_1][key1_1_1](gt,100),
        _[key2][key2_1](value(ne,"UNKNOWN") ^AND^ size(lte,32))
    );

// validate objects
Class1 obj1;
Class2 obj2;

validate(obj1,v1);
validate(obj2,v1);

Introduction

cpp-validator is a modern C++ header-only library for validation of variables, objects and containers.

The library can be used to validate:

  • plain variables;
  • properties of objects, where a property can be accessed either as object's variable or object's method;
  • contents and properties of containers;
  • nested containers and objects;
  • heterogeneous containers such as pairs and tuples;
  • trees;
  • results of evaluations or value transformations.

Basic usage of the library includes two steps:

  • first, define a validator using almost declarative syntax;
  • then, apply the validator to data that must be validated and check the results.

The library is suitable for both post-validation and pre-validation. Post-validation stands for validating the object that is already populated with the data. Pre-validation stands for validating the data before updating the object. The same validator declaration can be used in both cases.

There are a lot of options for cpp-validator extension and customization. During validation a text report describing an error can be constructed. Reports can be widely customised and translated to desired languages.

The library is tested with Clang, GCC and MSVC compilers that support C++14 or C++17 standards. Tested operating systems include Linux, Windows, macOS, iOS and Android platforms.

For more details see Documentation.

Some examples of basic usage

Check nested container elements and print report

> nested_map={ {"field1",{{1,5},{2,50}}}, {"field3",{}} }; validate(nested_map,v,err); if (err) { std::cerr << err.message() << std::endl; /* prints: "element #1 of field1 must be in range [10, 20, 30, 40, 50]" */ } ">
// define compound validator of nested container elements
auto v=validator(
                _["field1"][0](lt,100),
                _["field1"][1](in,range({10,20,30,40,50})),
                _["field2"](exists,false),
                _["field3"](empty(flag,true))
            );
                
error_report err;

// apply validator to container and construct error message

std::mapsize_t,size_t>> nested_map={
            {"field1",{{1,5},{2,50}}},
            {"field3",{}}
        };
validate(nested_map,v,err);
if (err)
{
    std::cerr << err.message() << std::endl;
    /* prints:
    
    "element #1 of field1 must be in range [10, 20, 30, 40, 50]"
    
    */
}

Check if value is greater than constant

Error as an argument.

// define validator
auto v=validator(gt,100);

// validate variables
error err;

validate(90,v,err);
if (err)
{
  // validation failed
}

validate(200,v,err))
if (!err)
{
  // validation succeeded
}

Error as an exception.

// define validator
auto v=validator(gt,100);

// validate variables

try
{
    validate(200,v); // succeed
    validate(90,v); // throw
}
catch (const validation_error& err)
{
    std::cerr << err.what() << std::endl;
    /* prints:
    
    "must be greater than 100"
    
    */
}

Explicit applying of a validator.

// define validator
auto v=validator(gt,100);

// apply validator to variables

int value1=90;
if (!v.apply(value1))
{
  // validation failed
}

int value2=200;
if (v.apply(value2))
{
  // validation succeeded
}

Check if string is greater than or equal to other string and size of the string is less than constant

// define validator
auto v=validator(
  value(gte,"sample string"),
  size(lt,15)
);

// apply validator to variables

std::string str1="sample";
if (!v.apply(str1))
{
  // validation failed, string is less than sample string
}

std::string str2="sample string+";
if (v.apply(str2))
{
  // validation succeeded
}

std::string str3="too long sample string";
if (!v.apply(str3))
{
  // validation failed, string is too long
}

Check if value is in interval and print report

// define validator
auto v=validator(in,interval(95,100));

// apply validator to variable and construct validation error message

error_report err;

size_t val=90;
validate(val,v,err);
if (err)
{
    std::cerr << err.mesage() << std::endl; 
    /* prints:
    
    "must be in interval [95,100]"
    
    */
}

Check container element and print report

test_map={{"field1","value1"}}; validate(test_map,v,err); if (err) { std::cerr << err.message() << std::endl; /* prints: "field1 must be greater than or equal to xxxxxx OR size of field1 must be greater than or equal to 100 OR field1 must be greater than or equal to zzzzzzzzzzzz" */ } ">
// define compound validator of container elements
auto v=validator(
                _["field1"](gte,"xxxxxx")
                 ^OR^
                _["field1"](size(gte,100) ^OR^ value(gte,"zzzzzzzzzzzz"))
            );

// apply validator to container and construct validation error message

error_report err;
std::map test_map={{"field1","value1"}};
validate(test_map,v,err);
if (err)
{
    std::cerr << err.message() << std::endl;
    /* prints:
    
    "field1 must be greater than or equal to xxxxxx OR size of field1 must be greater than or equal to 100 OR field1 must be greater than or equal to zzzzzzzzzzzz"
    
    */
}

Check custom object property and print report

// define structure with getter method
struct Foo
{
    bool red_color() const
    {
        return true;
    }
};

// define custom property
DRACOSHA_VALIDATOR_PROPERTY_FLAG(red_color,"Must be red","Must be not red");

// define validator of custom property
auto v=validator(
    _[red_color](flag,false)
);

// apply validator to object with custom property and construct validation error message

error_report err;
Foo foo_instance;
validate(foo_instance,v,err);
if (err)
{
    std::cerr << err.message() << std::endl;
    /* prints:
    
    "Must be not red"
    
    */
}

Pre-validate data before updating object's member

// define structure with member variables and member setter method
struct Foo
{
    std::string bar_value;
    
    uint32_t other_value;
    size_t some_size;
    
    void set_bar_value(std::string val)
    {
        bar_value=std::move(val);
    }
};

// define custom properties
DRACOSHA_VALIDATOR_PROPERTY(bar_value);
DRACOSHA_VALIDATOR_PROPERTY(other_value);

// template specialization for setting bar_value member of Foo
DRACOSHA_VALIDATOR_NAMESPACE_BEGIN

template <>
struct set_member_t
{
    template <typename ObjectT, typename MemberT, typename ValueT>
    void operator() (
            ObjectT& obj,
            MemberT&&,
            ValueT&& val
        ) const
    {
        obj.set_bar_value(std::forward(val));
    }
};

DRACOSHA_VALIDATOR_NAMESPACE_END

using namespace DRACOSHA_VALIDATOR_NAMESPACE;

// define validator of custom properties
auto v=validator(
    _[bar_value](ilex_ne,"UNKNOWN"), // case insensitive lexicographical not equal
    _[other_value](gte,1000)
);

Foo foo_instance;

error_report err;

// call setter with valid data
set_validated(foo_instance,bar_value,"Hello world",v,err);
if (!err)
{
    // object's member is set
}

// call setter with invalid data
set_validated(foo_instance,bar_value,"unknown",v,err);
if (err)
{
    // object's member is not set
    std::cerr << err.message() << std::endl;
    /* prints:
     
     "bar_value must be not equal to UNKNOWN"
     
     */
}
You might also like...
This data is a sample data created for a Qiita article and a YouTube commentary.
This data is a sample data created for a Qiita article and a YouTube commentary.

NiagaraSample UE4 4.27.1 English This data is a sample data created for a Qiita article and a YouTube commentary. Here is some sample data that may be

A Windows only library for allowing C++ to feel like C#

System What is this? C++ System is a header and Windows only library for interacting with the machine, in the way that C# does, but since C++ is not r

Dead simple C logging library contained in a single header (.h) file
Dead simple C logging library contained in a single header (.h) file

Seethe Logging so simple, you only need to include a single header file. seethe supports 6 different log levels (DEBUG, INFO, NOTICE, WARNING, ERROR,

RapidObj is an easy-to-use, single-header C++17 library that loads and parses Wavefront .obj files.

RapidObj About Integration Prerequisites Manual Integration CMake Integration API RapidObj Result Next Steps OS Support Third Party Tools and Resource

A C library for parsing/normalizing street addresses around the world. Powered by statistical NLP and open geo data.
A C library for parsing/normalizing street addresses around the world. Powered by statistical NLP and open geo data.

libpostal: international street address NLP libpostal is a C library for parsing/normalizing street addresses around the world using statistical NLP a

C ANSI Library to work with BER-TLV format data.
C ANSI Library to work with BER-TLV format data.

BER-TLV Challenge Library As requested a shared library(.so) were developed using C programming language to interpret and works with BER-TLV objects.

Stripped down version of BIGTREETECH-TouchScreenFirmware which only supports ST7920 emulation (Marlin Mode)

Stripped down version of BIGTREETECH-TouchScreenFirmware which only supports ST7920 emulation (Marlin Mode). This project only uses peripheral drivers supplied by the screen manufacturer and uses it's own library to parse the ST7920 commands.

You Only Look Twice: Rapid Multi-Scale Object Detection In Satellite Imagery
You Only Look Twice: Rapid Multi-Scale Object Detection In Satellite Imagery

YOLT You Only Look Twice: Rapid Multi-Scale Object Detection In Satellite Imagery As of 24 October 2018 YOLT has been superceded by SIMRDWN YOLT is an

curl4cpp - single header cURL wrapper for C++ around libcURL.

curl4cpp - single header cURL wrapper for C++ around libcURL.

Comments
Releases(v2.0.3)
Owner
Evgeny Sidorov
Evgeny Sidorov
Small header-only C++ library that helps to initialize Vulkan instance and device object

Vulkan Extensions & Features Help, or VkExtensionsFeaturesHelp, is a small, header-only, C++ library for developers who use Vulkan API.

Adam Sawicki 11 Oct 12, 2022
match(it): A lightweight header-only pattern-matching library for C++17 with macro-free APIs.

match(it): A lightweight header-only pattern-matching library for C++17 with macro-free APIs. Features Easy to get started. Single header library. Mac

Bowen Fu 434 Dec 27, 2022
a compile-time, header-only, dimensional analysis and unit conversion library built on c++14 with no dependencies.

UNITS A compile-time, header-only, dimensional analysis library built on c++14 with no dependencies. Get in touch If you are using units.h in producti

Nic Holthaus 809 Dec 29, 2022
A header only C++ library that provides type safety and user defined literals for physical units

SI - Type safety for physical units A header only c++ library that provides type safety and user defined literals for handling pyhsical values defined

Dominik Berner 399 Dec 25, 2022
Small Header only library to parse argv for flags

Small Header only library to parse argv for flags

Ben Wernicke 3 Nov 9, 2022
Header only roguelike rendering library.

Header only roguelike rendering library. Support for Opengl33 and Raylib. Features Support for custom glyph atlasses with up to 65655 tiles of custom

Journeyman 9 Dec 15, 2022
A Header-Only Engine that tries to use SFML in a deeper level

⚙️ SFML-Low-Level-Engine ⚙️ A header-only library that tries to use SFML at a deeper level ?? Instalation Download the source code and put the GLD fol

!Gustavo! 4 Aug 27, 2021
Header-only C++20 wrapper for MPI 4.0.

MPI Modern C++20 message passing interface wrapper. Examples Initialization: mpi::environment environment; const auto& communicator = mpi::world_c

Ali Can Demiralp 29 Apr 8, 2022
Header-only lock-free synchronization utilities (one writer, many readers).

stupid Header-only lock-free synchronization utilities (one writer, many readers). No queues Base functionality The base functionality of this library

Colugo 14 Nov 28, 2022
This is a Header-only c++ string implentation which specializes in dealing with small strings. 🧵

string-impl This is a Header-only c++ string implentation which specializes in dealing with small strings. ?? Usage ⌨ Instantiation A string can be in

null 1 Oct 26, 2022