Derivative containers based on otl_value<T> have built-in
NULL indicator functionality, that is, which can carry over the
NULL value from one operation to another.
Derivative data containers based on
otl_compact_value<T,null> have NULL value encoded via the
second template parameter, and carry over the NULL value from one
operation to another.
For example, a value gets read from a SELECT statement into a
otl_value<int> / otl_compact_value<int,-1> container,
and, it is a NULL. Then, the same value gets written into an
INSERT statement. The otl_value<int> /
otl_compact_value<int,-1> container retains the NULL, and
the NULL gets carried over from the SELECT into the INSERT.-1
represents NULL value is this example.
The otl_value<T> / otl_compact_value<T,null> classes
can be activated with #define OTL_STL,
or #define OTL_VALUE_TEMPLATE_ON.
template<class TData>
class otl_value{
public:
Function
/ Data member |
Description |
TData v; |
Container for a scalar value of the TData type. |
bool ind; |
NULL indicator. |
otl_value(); |
Default constructor. |
otl_value(const otl_value<TData>& var); | Copy constructor. |
otl_value(const TData& var); |
Copy constructor. |
otl_value(const otl_null var); |
Copy constructor. |
otl_value<TData>& operator= |
Assignment operator. |
otl_value<TData>& operator= |
Assignment operator. |
otl_value<TData>& operator= |
Assignment operator. |
bool is_null(void) const; |
Returns true if the otl_value is NULL. |
void set_null(void); |
Sets the otl_value to NULL. |
void set_null(const bool null); | Sets the otl_value to NULL if the null parameter is true, otherwise sets the otl_value as to having a non-NULL value. |
void set_non_null(void); |
sets the otl_value to non-NULL. |
}; // end of otl_value
template<class TData, const TData null_value>
class otl_compact_value{
public:
// null_value is a constant of the TData type that encodes NULL value for each instantiation of the template.
Function
/ Data member |
Description |
TData v; |
Container for scalar value of the TData type. |
otl_compact_value(); | Default constructor. |
otl_compact_value(const otl_compact_value<TData,null_value>& var); | Copy constructor. |
otl_compact_value(const TData& var); | Copy constructor. |
otl_compact_value(const otl_null var); | Copy constructor. |
otl_compact_value<TData,null_value>& operator= | Assignment operator. |
otl_compact_value<TData,null_value>& operator= | Assignment operator. |
otl_compact_value<TData,null_value>& operator= | Assignment operator. |
bool is_null(void) const; | Returns true if the otl_compact_value contains a value that is equal to null_value (the second template parameter). |
void set_null(const bool null); | Sets the otl_compact_value to NULL if "null" parameter is true. Otherwise does nothing. |
}; // end of otl_compact_valueAlso, four global template stream operators were overloaded for otl_value<T> / otl_compact_value<T,null> and otl_stream's:
template <class TData>These template overloaded operators can be used in a combination with the otl_value<T> / otl_compact_value<T,null> containers. The operators carry over NULL values, if there are any.
otl_stream& operator<<(otl_stream& s, const otl_value<TData>& var);
template <class TData>
otl_stream& operator>>(otl_stream& s, otl_value<TData>& var);
template <class TData, const TData null_value>
otl_stream& operator<<(otl_stream& s, const otl_compact_value<TData,null_value>& var);
template <class TData, const TData null_value>
otl_stream& operator>>(otl_stream& s, otl_compact_value<TData,null_value>& var);
Here's two more overloaded operator<< for writing otl_value<T> / otl_compact_value<T,null>'s to std::ostream:
template <class TData> std::ostream&
operator<<(std::ostream& s, const otl_value<TData>& var);
template <class TData, const TData null_value> std::ostream&
operator<<(std::ostream& s, const otl_compact_value<TData,null_value>& var);
As the name suggests, otl_compact_value<T,null> is more memory efficient compared with otl_value<T> (sizeof(otl_compact_value<T>) == sizeof(T), sizeof(otl_value<T>) == sizeof(T) + sizeof(bool) + padding). otl_compact_value<T,null> should be used when there is a magic value for type T that can represent NULL value in a given context. There may be many instantiations of otl_compact_value with the same T but with different null_values. otl_compact_value<T,null> can be used with non-integral types, C++11 compilers and higher, when otl_compact_value's second template non-type parameter has external linkage and operator==() defined for type T (first template parameter).
For more detail on otl_compact_value<T,null>, see code examples 98, 99, and 100. If you can't find a code example for your database type, it's easy to migrate the otl_compact_value based code from examples for other database types.
OTL can also be used with std::experimental::optional<T> / std::optional<T> and the following data types: int, unsigned, long, short, float, double, otl_datetime (OTL date&time container), and std::string (STL string class, requires #define OTL_STL). , OTL defines operators>>/<< that work with the optional<T> templates as template member functions of the otl_stream class, when #define OTL_CPP_14_ON or #define OTL_CPP_17_ON is auto or explicitly enabled, and when #define OTL_STREAM_WITH_STD_OPTIONAL_ON is enabled:
class otl_stream{...
#if defined(OTL_STREAM_WITH_STD_OPTIONAL_ON)
template<typename TData, template <typename> class Optional> otl_stream &operator<<(const Optional<TData> &var);
template<typename TData, template <typename> class Optional> otl_stream &operator>>(Optional<TData> &var);
#endif
...
};
When frac_precision of otl_datetime is different from its default value of 0, and when std::optional<otl_datetime> is used to read values from an otl_stream, such variables should be initialized, for example:
std::optional<otl_datetime> f(otl_datetime(2016,10,27,13,21,15,6,123456));
The reason is that OTL uses the frac_precision of an otl_datetime container that OTL writes into (a.k.a output variable) to format the output otl_datetime::fraction correctly.
OTL utilizes the buffer of a std::optional<T> variable (a.k.a. direct use, no temporaries, more efficient), if the variable has a value (!= std::nullopt). In the case of an empty (==std::nullopt) std::optional<T> variable, OTL uses std::move() when a move constructor is available for type T.
When OTL writes (operator <<) an optional<> variable and
it has no value, OTL treats the absence of value as NULL and writes the NULL into the database. And when OTL
reads (operator>>) NULL from the database, OTL sets the output optional<>
variable to "no value", which can be checked with optional<T>::operator bool() or has_value() function.
For more detail on std::optional<T>, see code examples 98, 99, 100, and 204. If you can't find a code example for your database type, it's easy to migrate the otl_compact_value based code from examples for other database types.
Copyright © 1996-2023, Sergei Kuchin, email: skuchin@gmail.com, skuchin@gmail.com .
Permission to use, copy, modify and redistribute this document for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.