“The devil is in the details”
A particularity of the C++ data validation concept I wrote about is passing that wrapped_value
object as an argument to a function. The reason is for that wrapped value to behave like the type it wraps so that it has a natural usage. I should not know the actual value is hidden by a level of indirection, making it easy to control any mutation.
To achieve that feature, I have used the user-defined conversion function and it went smooth. I can have a wrapped value and pass it as an argument (by value or const reference) to a function:
int inc_by_value(int v) { return v + 1; } int inc_by_const_ref(const int& a) { return a + 1; } msd::wrapped_value<int, UpperBoundLimiter<int, 10>> value = 2; assert(inc_by_value(value) == 3); assert(inc_by_const_ref(value) == 3);
Pass by non-const reference
The user-defined conversion I initially implemented is the const reference overload:
operator const Value&() const noexcept { return value_; }
That’s why I can pass the wrapped value by value and const reference. To pass it as a non-const reference, I have to implement the non-const reference overload:
template <typename Value, typename... Wrappers> class wrapped_value { // ... operator Value&() noexcept { return value_; } // ... }
And I can pass by reference and mutate the value:
void inc_by_ref(int& a) { ++a; } msd::wrapped_value<int, UpperBoundLimiter<int, 10>> value = 2; inc_by_ref(value); assert(value == 3);
or
void update(int& a) { a = 20; } update(value); assert(value == 10); // should be 10 because of the UpperBoundLimiter
But the last assertion fails: Assertion `value == 10′ failed. Continue reading To break or not to break… encapsulation