Click here to Skip to main content
Click here to Skip to main content

Trusted Variables

, 29 Apr 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
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.

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

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

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

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").

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 ! Smile | :)

License

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

Share

About the Author

Evgeny Zavalkovsky
Software Developer (Senior) Box
United States United States
No Biography provided

Comments and Discussions

 
QuestionBetter than enums? PinmemberMember 969253030-Apr-14 22:36 
AnswerRe: Better than enums? [modified] PinmemberEvgeny Zavalkovsky1-May-14 6:21 
QuestionWe Think Along Similar Lines PinmemberEnigmatic Texan27-Apr-14 19:52 
AnswerRe: We Think Along Similar Lines PinmemberEvgeny Zavalkovsky1-May-14 14:07 
SuggestionPlease eliminate the #define statements! [modified] PinmemberStefan_Lang24-Apr-14 21:15 
GeneralRe: Please eliminate the #define statements! PinmemberEvgeny Zavalkovsky25-Apr-14 8:33 
GeneralRe: Please eliminate the #define statements! PinmemberStefan_Lang27-Apr-14 21:13 
GeneralRe: Please eliminate the #define statements! PinmemberEvgeny Zavalkovsky27-Apr-14 21:29 
GeneralRe: Please eliminate the #define statements! PinmemberBill_Hallahan1-May-14 19:15 
GeneralRe: Please eliminate the #define statements! PinmemberEvgeny Zavalkovsky2-May-14 5:59 
GeneralRe: Please eliminate the #define statements! [modified] PinmemberBill_Hallahan2-May-14 16:56 
GeneralRe: Please eliminate the #define statements! PinmemberEvgeny Zavalkovsky2-May-14 18:40 
Questionmore safer defination Pinmemberjacky_zz21-Apr-14 17:50 
AnswerRe: more safer defination PinmemberEvgeny Zavalkovsky21-Apr-14 21:06 
GeneralRe: more safer defination PinmemberPrakash Nadar29-Apr-14 8:18 
GeneralRe: more safer defination PinmemberEvgeny Zavalkovsky1-May-14 14:07 
GeneralMy 5 Pinmemberkwanti20-Apr-14 8:10 
GeneralMy vote of 5 PinmemberDavid Days17-Apr-14 8:37 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141216.1 | Last Updated 29 Apr 2014
Article Copyright 2014 by Evgeny Zavalkovsky
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid