c++ data-access-layer nullable

Valores anulables en C++



data-access-layer nullable (4)

Hay muchas implementaciones de tipo Nullable para C ++ y la mayoría están incompletas. En el mundo de C ++, los tipos anulables se denominan tipos opcionales . Esto fue propuesto para C ++ 14 pero se pospuso. Sin embargo, el código para implementarlo compila y funciona en la mayoría de los compiladores de C ++ 11. Solo puede colocar el archivo de encabezado único implementando un tipo opcional y comenzar a usarlo:

https://raw.githubusercontent.com/akrzemi1/Optional/master/optional.hpp

Uso de la muestra:

#if (defined __cplusplus) && (__cplusplus >= 201700L) #include <optional> #else #include "optional.hpp" #endif #include <iostream> #if (defined __cplusplus) && (__cplusplus >= 201700L) using std::optional; #else using std::experimental::optional; #endif int main() { optional<int> o1, // empty o2 = 1, // init from rvalue o3 = o2; // copy-constructor if (!o1) { cout << "o1 has no value"; } std::cout << *o2 << '' '' << *o3 << '' '' << *o4 << ''/n''; }

Más documentación: http://en.cppreference.com/w/cpp/experimental/optional

También vea mi otra respuesta: https://stackoverflow.com/a/37624595/207661

Estoy creando una capa de acceso a la base de datos en C ++ nativo, y estoy buscando formas de admitir valores NULL. Aquí está lo que tengo hasta ahora:

class CNullValue { public: static CNullValue Null() { static CNullValue nv; return nv; } }; template<class T> class CNullableT { public: CNullableT(CNullValue &v) : m_Value(T()), m_IsNull(true) { } CNullableT(T value) : m_Value(value), m_IsNull(false) { } bool IsNull() { return m_IsNull; } T GetValue() { return m_Value; } private: T m_Value; bool m_IsNull; };

Así es como tendré que definir funciones:

void StoredProc(int i, CNullableT<int> j) { ...connect to database ...if j.IsNull pass null to database etc }

Y lo llamo así:

sp.StoredProc(1, 2);

o

sp.StoredProc(3, CNullValue::Null());

Me preguntaba si había una manera mejor que esta. En particular, no me gusta el objeto singular de CNullValue con las estadísticas. Prefiero hacer solo

sp.StoredProc(3, CNullValue);

o algo similar. ¿Cómo resuelven los demás este problema?


Reemplace IsNull con HasValue y obtendrá el tipo .NET Nullable .

Por supuesto .. esto es C ++. ¿Por qué no usar simplemente un puntero a un tipo "primitivo"?


Boost.Optional probablemente hace lo que necesitas.

boost::none toma el lugar de su CNullValue::Null() . Ya que es un valor en lugar de una llamada a la función miembro, puede hacerlo using boost::none; Si quieres, por brevedad. Tiene una conversión a bool lugar de IsNull , y operator* lugar de GetValue , por lo que harías:

void writeToDB(boost::optional<int> optional_int) { if (optional_int) { pass *optional_int to database; } else { pass null to database; } }

Pero creo que lo que se te ocurrió es esencialmente el mismo diseño.


EDITAR : Mejorado con excepción de lanzamiento en el valor "nulo". Más arreglos

Si boost no es una opción, en c ++ 11 también puede aprovechar nullptr y nullptr_t typedef para crear un Nullable<T> con Nullable<T> con casi la misma semántica que .NET.

#pragma once #include <cstddef> #include <stdexcept> template <typename T> class Nullable final { public: Nullable(); Nullable(const T &value); Nullable(nullptr_t nullpointer); const Nullable<T> & operator=(const Nullable<T> &value); const Nullable<T> & operator=(const T &value); const Nullable<T> & operator=(nullptr_t nullpointer); bool HasValue() const; const T & GetValueOrDefault() const; const T & GetValueOrDefault(const T &def) const; bool TryGetValue(T &value) const; public: class NullableValue final { public: friend class Nullable; private: NullableValue(); NullableValue(const T &value); public: NullableValue & operator=(const NullableValue &) = delete; operator const T &() const; const T & operator*() const; const T * operator&() const; // https://.com/questions/42183631/inability-to-overload-dot-operator-in-c const T * operator->() const; public: template <typename T2> friend bool operator==(const Nullable<T2> &op1, const Nullable<T2> &op2); template <typename T2> friend bool operator==(const Nullable<T2> &op, const T2 &value); template <typename T2> friend bool operator==(const T2 &value, const Nullable<T2> &op); template <typename T2> friend bool operator==(const Nullable<T2> &op, nullptr_t nullpointer); template <typename T2> friend bool operator!=(const Nullable<T2> &op1, const Nullable<T2> &op2); template <typename T2> friend bool operator!=(const Nullable<T2> &op, const T2 &value); template <typename T2> friend bool operator!=(const T2 &value, const Nullable<T2> &op); template <typename T2> friend bool operator==(nullptr_t nullpointer, const Nullable<T2> &op); template <typename T2> friend bool operator!=(const Nullable<T2> &op, nullptr_t nullpointer); template <typename T2> friend bool operator!=(nullptr_t nullpointer, const Nullable<T2> &op); private: void checkHasValue() const; private: bool m_hasValue; T m_value; }; public: NullableValue Value; }; template <typename T> Nullable<T>::NullableValue::NullableValue() : m_hasValue(false), m_value(T()) { } template <typename T> Nullable<T>::NullableValue::NullableValue(const T &value) : m_hasValue(true), m_value(value) { } template <typename T> Nullable<T>::NullableValue::operator const T &() const { checkHasValue(); return m_value; } template <typename T> const T & Nullable<T>::NullableValue::operator*() const { checkHasValue(); return m_value; } template <typename T> const T * Nullable<T>::NullableValue::operator&() const { checkHasValue(); return &m_value; } template <typename T> const T * Nullable<T>::NullableValue::operator->() const { checkHasValue(); return &m_value; } template <typename T> void Nullable<T>::NullableValue::checkHasValue() const { if (!m_hasValue) throw std::exception("Nullable object must have a value"); } template <typename T> bool Nullable<T>::HasValue() const { return Value.m_hasValue; } template <typename T> const T & Nullable<T>::GetValueOrDefault() const { return Value.m_value; } template <typename T> const T & Nullable<T>::GetValueOrDefault(const T &def) const { if (Value.m_hasValue) return Value.m_value; else return def; } template <typename T> bool Nullable<T>::TryGetValue(T &value) const { value = Value.m_value; return Value.m_hasValue; } template <typename T> Nullable<T>::Nullable() { } template <typename T> Nullable<T>::Nullable(nullptr_t nullpointer) { (void)nullpointer; } template <typename T> Nullable<T>::Nullable(const T &value) : Value(value) { } template <typename T2> bool operator==(const Nullable<T2> &op1, const Nullable<T2> &op2) { if (op1.Value.m_hasValue != op2.Value.m_hasValue) return false; if (op1.Value.m_hasValue) return op1.Value.m_value == op2.Value.m_value; else return true; } template <typename T2> bool operator==(const Nullable<T2> &op, const T2 &value) { if (!op.Value.m_hasValue) return false; return op.Value.m_value == value; } template <typename T2> bool operator==(const T2 &value, const Nullable<T2> &op) { if (!op.Value.m_hasValue) return false; return op.Value.m_value == value; } template <typename T2> bool operator==(const Nullable<T2> &op, nullptr_t nullpointer) { (void)nullpointer; return !op.Value.m_hasValue; } template <typename T2> bool operator==(nullptr_t nullpointer, const Nullable<T2> &op) { (void)nullpointer; return !op.Value.m_hasValue; } template <typename T2> bool operator!=(const Nullable<T2> &op1, const Nullable<T2> &op2) { if (op1.Value.m_hasValue != op2.Value.m_hasValue) return true; if (op1.Value.m_hasValue) return op1.Value.m_value != op2.Value.m_value; else return false; } template <typename T2> bool operator!=(const Nullable<T2> &op, const T2 &value) { if (!op.Value.m_hasValue) return true; return op.Value.m_value != value; } template <typename T2> bool operator!=(const T2 &value, const Nullable<T2> &op) { if (!op.Value.m_hasValue) return false; return op.Value.m_value != value; } template <typename T2> bool operator!=(const Nullable<T2> &op, nullptr_t nullpointer) { (void)nullpointer; return op.Value.m_hasValue; } template <typename T2> bool operator!=(nullptr_t nullpointer, const Nullable<T2> &op) { (void)nullpointer; return op.Value.m_hasValue; } template <typename T> const Nullable<T> & Nullable<T>::operator=(const Nullable<T> &value) { Value.m_hasValue = value.Value.m_hasValue; Value.m_value = value.Value.m_value; return *this; } template <typename T> const Nullable<T> & Nullable<T>::operator=(const T &value) { Value.m_hasValue = true; Value.m_value = value; return *this; } template <typename T> const Nullable<T> & Nullable<T>::operator=(nullptr_t nullpointer) { (void)nullpointer; Value.m_hasValue = false; Value.m_value = T(); return *this; }

Lo probé en gcc, clang y VS15 con lo siguiente:

#include <iostream> using namespace std; int main(int argc, char* argv[]) { (void)argc; (void)argv; Nullable<int> ni1; Nullable<int> ni2 = nullptr; Nullable<int> ni3 = 3; Nullable<int> ni4 = 4; ni4 = nullptr; Nullable<int> ni5 = 5; Nullable<int> ni6; ni6 = ni3; Nullable<int> ni7(ni3); //Nullable<int> ni8 = NULL; // This is an error in gcc/clang but it''s ok in VS12 cout << (ni1 == nullptr ? "True" : "False") << endl; // True cout << (ni2 == nullptr ? "True" : "False") << endl; // True cout << (ni2 == 3 ? "True" : "False") << endl; // False cout << (ni2 == ni3 ? "True" : "False") << endl; // False cout << (ni3 == 3 ? "True" : "False") << endl; // True cout << (ni2 == ni4 ? "True" : "False") << endl; // True cout << (ni3 == ni5 ? "True" : "False") << endl; // False cout << (ni3 == ni6 ? "True" : "False") << endl; // True cout << (ni3 == ni7 ? "True" : "False") << endl; // True //cout << ni1 << endl; // Doesn''t compile //cout << ni3 << endl; // Doesn''t compile cout << ni3.Value << endl; // 3 //cout << ni1.Value << endl; // Throw exception //cout << ni2.Value << endl; // Throw exception //ni3.Value = 2; // Doesn''t compile cout << sizeof(ni1) << endl; // 8 on VS15 return 0; }