Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C++

Trusted Variables

Rate me:
Please Sign up or sign in to vote.
4.70/5 (24 votes)
29 Apr 2014CPOL3 min read 30.2K   213   27   18
An easy and an elegeant way to create "trusted" data variables.

Introduction

"Trusted Variables" are intended to simplify three very common coding approaches:

  1. Set variable default value.
  2. Define range of valid values that may be assigned to the variable.
  3. Validate assignment (assigned value is in the range) for any assignment.

Usually, these three approaches are implemented in different places: default values are set on declaration or initialization, range definition is semantic only, and assignment validation is implemented for any particular assignment.

"Trusted variables" are trying to bring all three approaches together to the single place - declaration. Once "Trusted variable" is declared, default value is set, and any trial to assign "not in range" value will fail.

Example

Let's assume we have some application, that uses calendar day, as a variable..

Ideally, we would like to define some "trusted" day, that may be used without any further verification.

C++
// variable "day" declaration, type set as uint8_t, range 1-31, and default value 15
TRUSTED_VAR( uint8_t, day, MIN(1), MAX(31), DEFAULT(15) ); 

Then, when we use, we may refer to variable day, as a native uint8_t.

C++
// May be used as a native type ( uint8_t in this case ) Lvalue in any expression
day = 10; // ok, value in range

uint8_t wrong_day = 32;
 
day = wrong_day; // out of range, error generated, "day" was not changed

// May be used as a native type ( uint8_t in this case ) Rvalue in any expression
uint8_t temp_day = day;

The Idea

The idea of trusted variable is pretty simple:

For assignment verification, Copy Constructor and Assignment Operator, should involve verification, and for using "like" a native type, Cast Operator to the native type should be implemented.

Implementation

Please NOTE, the following code contains "extra" information like a variable name, and other "string" related conversions, that may be omitted.

C++
#ifndef __TRUSTED_VAR__
#define __TRUSTED_VAR__
 
#include <string>
// Following constexpr functions are for decorations purposes only
template < typename T > static constexpr T MIN(T min) { return min; }
template < typename T > static constexpr T MAX(T max) { return max; }
template < typename T > static constexpr T DEFAULT(T default) { return default; }

#define TRUSTED_VAR( _type, _name, _min, _max, _default ) \
            TrustedVar< _type,_min, _max, _default > _name{#_name};
 
template 
< typename VAR_TYPE, VAR_TYPE MIN_VAL, VAR_TYPE MAX_VAL, VAR_TYPE DEFAULT_VAL > 
class TrustedVar
{
public:
    static_assert((MAX_VAL >= MIN_VAL), "Invalid Range");
 
    explicit TrustedVar(const char* _val_name):val_name{_val_name}, value{DEFAULT_VAL}
    {}
 
    TrustedVar(const TrustedVar& _var)
    {
        value = _var.value;
    }
 
    TrustedVar& operator = (const VAR_TYPE& _value)
    {
        if ( IsInRange(_value) ) value = _value;
 
        else NotInRange( val_name,
                         std::to_string(MIN_VAL), 
                         std::to_string(MAX_VAL),
                         std::to_string(_value)
                   );
 
        return *this;
    }
 
    operator VAR_TYPE()
    {
        return value;
    }
 
private:
    bool IsInRange(const VAR_TYPE& _value)
    {
        return ((MIN_VAL <= _value) && (MAX_VAL >= _value));
    }
private:
    const std::string val_name;
    VAR_TYPE value;
};
 
#endif //__TRUSTED_VAR__

I did use MACRO definition for TRUSTED_VAR for this particular example I wanted to pass variable name for logging purposes (_name{#_name})

If that is not required - that single MACRO may be omitted.

Error Handling

As we saw, on invalid (out of range) assignment will generate an error. The question is how this error will be handled? That really depends on the application, that uses Trusted Variable. As an option, "Out of Range" assignment may generate an exception, may trigger "assert", may generate error log, may set last error, etc.

So ideally, we want to make Error Handling customizable.

Another interesting thing about Trusted Variables, is that usually they are not coming in "singles". In our example with a variable day, most likely we will have Trusted Variable month, Trusted Variable year, and may be hour, minute and second. All of them, I assume, will have the same Error Handling Method.

Providing Error Handling method, as a virtual method on definition of every Trusted Variable, will add another parameter, and more important, will add Memory Complexity (Virtual Table will be created for any single variable) and Performance Complexity (Error handling method will be resolved in Run time).

Instead of this, I suggest to use kind of Compile Time "Namespace Polymorphism".

Let's define Error Handling method, scoped by specific namespace.

C++
namespace DEFAULT_ERROR_HANDLING_NAMESPACE
{
    void NotInRange(const std::string& _var_name, 
                    const std::string& range_min, 
                    const std::string& range_max, 
                    const std::string& val)
    {
        std::cout << "ERROR: Variable \"" 
                      + std::string(_var_name) 
                      + "\":"  
                      + " value " 
                      + val 
                      + " is out of " 
                      + range_min 
                      + '-' 
                      + range_max 
                      + " range " << std::endl;
    }
}

Now, before using of Trusted Variable, we just need to declare using of the specific namespace, that contains "desired" Error handling implementation. Now all Trusted Variables, while we are "using" this specific namespace, will be handled by the same method (remember, Trusted Variables are not coming "alone" to the "party").

C++
using namespace DEFAULT_ERROR_HANDLING_NAMESPACE;
 
int main()
{
    // variable "day" declaration, type set as uint8_t, range 1-31, and default value 15
    TRUSTED_VAR( uint8_t, day, MIN(1), MAX(31), DEFAULT(15) );
    
    // May be used as a native type ( uint8_t in this case ) Lvalue in any expression
    day = 10; // ok, value in range

    uint8_t wrong_day = 32;
 
    day = wrong_day; // out of range, error generated, "day" was not changed

    // May be used as a native type ( uint8_t in this case ) Rvalue in any expression
    uint8_t temp_day = day;
 
    return 0;
}

As a result, on Out Of Range assignment,

ERROR: Variable "day": value 32 is out of 1-31 range 

will be generated.

Customization

Please see Trusted Variable implementation as a concept. The code may be easily customized, by omitting unnecessary information, like a variable name, removing string related operations, and/or change of NotInRange Error Handling signature.

I preferred to avoid run-time polymorphism.

Revisions

  • April 27, 2014 - constexpr functions instead of decoration macros were added (thanks to Stefan_Lang for the idea)

Compatibility

  • Tested on VS2013 + Visual C++ Compiler November 2013 CTP (for constexpr support)

Conclusion

Use them ! :)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Box
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionBetter than enums? Pin
Member 969253030-Apr-14 21:36
Member 969253030-Apr-14 21:36 
AnswerRe: Better than enums? Pin
Evgeny Zavalkovsky1-May-14 5:21
Evgeny Zavalkovsky1-May-14 5:21 
QuestionWe Think Along Similar Lines Pin
David A. Gray27-Apr-14 18:52
David A. Gray27-Apr-14 18:52 
AnswerRe: We Think Along Similar Lines Pin
Evgeny Zavalkovsky1-May-14 13:07
Evgeny Zavalkovsky1-May-14 13:07 
SuggestionPlease eliminate the #define statements! Pin
Stefan_Lang24-Apr-14 20:15
Stefan_Lang24-Apr-14 20:15 
GeneralRe: Please eliminate the #define statements! Pin
Evgeny Zavalkovsky25-Apr-14 7:33
Evgeny Zavalkovsky25-Apr-14 7:33 
GeneralRe: Please eliminate the #define statements! Pin
Stefan_Lang27-Apr-14 20:13
Stefan_Lang27-Apr-14 20:13 
GeneralRe: Please eliminate the #define statements! Pin
Evgeny Zavalkovsky27-Apr-14 20:29
Evgeny Zavalkovsky27-Apr-14 20:29 
GeneralRe: Please eliminate the #define statements! Pin
Bill_Hallahan1-May-14 18:15
Bill_Hallahan1-May-14 18:15 
GeneralRe: Please eliminate the #define statements! Pin
Evgeny Zavalkovsky2-May-14 4:59
Evgeny Zavalkovsky2-May-14 4:59 
GeneralRe: Please eliminate the #define statements! Pin
Bill_Hallahan2-May-14 15:56
Bill_Hallahan2-May-14 15:56 
GeneralRe: Please eliminate the #define statements! Pin
Evgeny Zavalkovsky2-May-14 17:40
Evgeny Zavalkovsky2-May-14 17:40 
Questionmore safer defination Pin
jackyxinli21-Apr-14 16:50
jackyxinli21-Apr-14 16:50 
AnswerRe: more safer defination Pin
Evgeny Zavalkovsky21-Apr-14 20:06
Evgeny Zavalkovsky21-Apr-14 20:06 
GeneralRe: more safer defination Pin
Prakash Nadar29-Apr-14 7:18
Prakash Nadar29-Apr-14 7:18 
GeneralRe: more safer defination Pin
Evgeny Zavalkovsky1-May-14 13:07
Evgeny Zavalkovsky1-May-14 13:07 
GeneralMy 5 Pin
kwanti20-Apr-14 7:10
kwanti20-Apr-14 7:10 
GeneralMy vote of 5 Pin
David Days17-Apr-14 7:37
professionalDavid Days17-Apr-14 7:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.