2010-03-29 7 views
24

네이티브 C++에서 데이터베이스 액세스 레이어를 만들고 NULL 값을 지원하는 방법을 찾고 있습니다.C++의 Nullable 값

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; 
}; 

이것은 내가 기능을 정의해야합니다 방법은 다음과 같습니다 :

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

을 그리고 다음과 같이 호출 : 여기에 지금까지 무엇을 가지고

sp.StoredProc(1, 2); 

또는

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

더 좋은 점이 있다면 궁금합니다. 그거야. 특히 나는 통계와 함께 CNullValue의 싱글 톤과 같은 객체를 좋아하지 않습니다. 그냥 할애하고 싶습니다.

sp.StoredProc(3, CNullValue); 

또는 그와 비슷한 것입니다. 다른 사람들이이 문제를 어떻게 해결합니까?

답변

27

Boost.Optional은 아마도 필요한 것을 수행합니다.

boost::noneCNullValue::Null()을 대신합니다. 멤버 함수 호출이 아닌 값이기 때문에 간결성을 위해 원하는 경우 using boost::none;을 수행 할 수 있습니다. 그것은 bool 대신 IsNull, 대신 GetValueoperator*에 변환을 가지고, 그래서 당신이 할 거라고 :

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

을하지만 당신이 왔어요 것은 본질적으로 같은 디자인, 나는 생각한다.

+0

특히 힙 할당을 사용하지 않으므로 성능면에서 임베디드 값과 동일하다는 점을 고려하십시오. –

+0

지금이 라이브러리를 검토해 주셔서 감사합니다. – DanDan

+0

방금 ​​테스트했습니다. 이거 완벽 해. – DanDan

3

IsNull을 으로 바꾸면 .NET Nullable 유형을 얻게됩니다.

물론 .. 이것은 C++입니다. 왜 "프리미티브"타입에 대한 포인터를 사용하지 않는가?

+8

포인터의 복사 의미가 다릅니다. 널이 아닌 포인터를 복사하면 사본은 동일한 오브젝트를 참조합니다. 이 CNullableT를 복사하면 사본에 값의 자체 인스턴스가 있습니다. 어떤 상황에서는 하나를 원하는 경우, 다른 옵션을 원할 수도 있지만, 그 값의 범위를 "임의의 T 또는 없음"으로 할 것인지 여부에 따라 독립적 인 문제가됩니다. 따라서 선택적 값에 대한 포인터를 사용하면 수하물이 생깁니다. –

+1

난 그냥 대부분의 경우에 대한 인터페이스 청소기를 만들려고 노력했다, 그래서 나는 StoredProcedcure 할 수있다 (1, 2) 대신 의 INT P = 2; StoredProcedure (1, &p); 예, .NET Nullable 유형이 영감으로 사용되었습니다. – DanDan

+0

@Steve Jessop : 우수하지 않은 점을 고려했습니다. – Randolpho

12

EDIT : "null"값의 예외 예외가 개선되었습니다. 더 많은 수정

부스트 당신은 또한 nullptr을 활용할 수 (11) C++의 옵션 및 .NET 것과 거의 동일한 의미와 Nullable<T>을 만들 수있는 nullptr_t 형식 정의가 아닌 경우

.

#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 &default) 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://stackoverflow.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 &default) const 
{ 
    if (Value.m_hasValue) 
     return Value.m_value; 
    else 
     return default; 
} 

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; 
} 

나는 다음과 GCC, 그 소리와 VS15에서 테스트 :

#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; 
} 
+0

! = 오버로드 중 하나에 오타가 있습니다. "if (! op.Value.true)" – AaronHS

+0

@AaronHS 감사합니다. 고정적이고 더 유용한 의미를 추가했습니다 (TryGet 메소드). – ceztko

2

가 C++에 대한 nullable 형식 구현의 많은이며, 대부분이 불완전. C++ 세계에서 null이 가능한 유형옵션 유형이라고합니다. 이것은 C++ 14에서 제안되었지만 연기되었습니다. 그러나 그것을 구현하는 코드는 대부분의 C++ 11 컴파일러에서 컴파일되고 작동합니다.당신은 단지 선택 유형을 구현하는 하나의 헤더 파일에 드롭하고 그것을 사용할 수 있습니다 :

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

샘플 사용 :

#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'; 
} 

더 많은 문서 : http://en.cppreference.com/w/cpp/experimental/optional

또한 내 다른 대답을 참조하십시오 https://stackoverflow.com/a/37624595/207661