mirror of
https://github.com/microsoft/GSL.git
synced 2024-11-03 17:56:43 -05:00
Dev/annagrin/remove explicit not null constructor (#743)
* Added c++17 test configurations for clang5.0 and clang6.0 * Removed explicit not_null constructor, sloppy_not_null, added strict_not_null We added explicit not_null constructor in version 2.0.0. It proved very difficult to switch to the new version for large code bases that adopted previous versions of gsl, due to not_null used extensively in the code. Still, using explicit constructor is very benefitial for new code, since it encorages better API design and make null checks intentional. To resolve the issue, this change: - removes explicit keyword from not_null constructor - removes unneded sloppy_not_null type - adds strict_not_null type to behave the same way as v2 not_null - updates tests * Removed explicit not_null constructor, sloppy_not_null, added strict_not_null We added explicit not_null constructor in version 2.0.0. It proved very difficult to switch to the new version for large code bases that adopted previous versions of gsl, due to not_null used extensively in the code. Still, using explicit constructor is very benefitial for new code, since it encorages better API design and make null checks intentional. To resolve the issue, this change: - removes explicit keyword from not_null constructor - removes unneded sloppy_not_null type - adds strict_not_null type to behave the same way as v2 not_null - updates tests * fixed build break for gcc7 * added more tests * added more non-compiling tests * Addressed PR comments and suppressed a code analysis warning * Fixed test failure in not_null tests
This commit is contained in:
parent
7a7d025ffa
commit
9ff6e19ea9
@ -412,6 +412,7 @@ namespace details
|
||||
return totalSize() / this->Base::totalSize();
|
||||
}
|
||||
|
||||
GSL_SUPPRESS(c.128) // NO-FORMAT: attribute // no pointers to BoundsRanges should be ever used
|
||||
constexpr size_type elementNum(std::size_t dim) const noexcept
|
||||
{
|
||||
if (dim > 0)
|
||||
|
@ -72,13 +72,13 @@ public:
|
||||
static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
|
||||
|
||||
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr explicit not_null(U&& u) : ptr_(std::forward<U>(u))
|
||||
constexpr not_null(U&& u) : ptr_(std::forward<U>(u))
|
||||
{
|
||||
Expects(ptr_ != nullptr);
|
||||
}
|
||||
|
||||
template <typename = std::enable_if_t<!std::is_same<std::nullptr_t, T>::value>>
|
||||
constexpr explicit not_null(T u) : ptr_(u)
|
||||
constexpr not_null(T u) : ptr_(u)
|
||||
{
|
||||
Expects(ptr_ != nullptr);
|
||||
}
|
||||
@ -121,7 +121,7 @@ private:
|
||||
|
||||
template <class T>
|
||||
auto make_not_null(T&& t) {
|
||||
return gsl::not_null<std::remove_cv_t<std::remove_reference_t<T>>>{std::forward<T>(t)};
|
||||
return not_null<std::remove_cv_t<std::remove_reference_t<T>>>{std::forward<T>(t)};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@ -189,6 +189,100 @@ struct hash<gsl::not_null<T>>
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace gsl
|
||||
{
|
||||
|
||||
//
|
||||
// strict_not_null
|
||||
//
|
||||
// Restricts a pointer or smart pointer to only hold non-null values,
|
||||
//
|
||||
// - provides a strict (i.e. explicit contructor from T) wrapper of not_null
|
||||
// - to be used for new code that wishes the design to be cleaner and make not_null
|
||||
// checks intentional, or in old code that would like to make the transition.
|
||||
//
|
||||
// To make the transition from not_null, incrementally replace not_null
|
||||
// by strict_not_null and fix compilation errors
|
||||
//
|
||||
// Expect to
|
||||
// - remove all unneded conversions from raw pointer to not_null and back
|
||||
// - make API clear by specifyning not_null in parameters where needed
|
||||
// - remove unnesessary asserts
|
||||
//
|
||||
template <class T>
|
||||
class strict_not_null: public not_null<T>
|
||||
{
|
||||
public:
|
||||
|
||||
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr explicit strict_not_null(U&& u) :
|
||||
not_null<T>(std::forward<U>(u))
|
||||
{}
|
||||
|
||||
template <typename = std::enable_if_t<!std::is_same<std::nullptr_t, T>::value>>
|
||||
constexpr explicit strict_not_null(T u) :
|
||||
not_null<T>(u)
|
||||
{}
|
||||
|
||||
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr strict_not_null(const not_null<U>& other) :
|
||||
not_null<T>(other)
|
||||
{}
|
||||
|
||||
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr strict_not_null(const strict_not_null<U>& other) :
|
||||
not_null<T>(other)
|
||||
{}
|
||||
|
||||
strict_not_null(strict_not_null&& other) = default;
|
||||
strict_not_null(const strict_not_null& other) = default;
|
||||
strict_not_null& operator=(const strict_not_null& other) = default;
|
||||
strict_not_null& operator=(const not_null<T>& other)
|
||||
{
|
||||
not_null<T>::operator=(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// prevents compilation when someone attempts to assign a null pointer constant
|
||||
strict_not_null(std::nullptr_t) = delete;
|
||||
strict_not_null& operator=(std::nullptr_t) = delete;
|
||||
|
||||
// unwanted operators...pointers only point to single objects!
|
||||
strict_not_null& operator++() = delete;
|
||||
strict_not_null& operator--() = delete;
|
||||
strict_not_null operator++(int) = delete;
|
||||
strict_not_null operator--(int) = delete;
|
||||
strict_not_null& operator+=(std::ptrdiff_t) = delete;
|
||||
strict_not_null& operator-=(std::ptrdiff_t) = delete;
|
||||
void operator[](std::ptrdiff_t) const = delete;
|
||||
};
|
||||
|
||||
// more unwanted operators
|
||||
template <class T, class U>
|
||||
std::ptrdiff_t operator-(const strict_not_null<T>&, const strict_not_null<U>&) = delete;
|
||||
template <class T>
|
||||
strict_not_null<T> operator-(const strict_not_null<T>&, std::ptrdiff_t) = delete;
|
||||
template <class T>
|
||||
strict_not_null<T> operator+(const strict_not_null<T>&, std::ptrdiff_t) = delete;
|
||||
template <class T>
|
||||
strict_not_null<T> operator+(std::ptrdiff_t, const strict_not_null<T>&) = delete;
|
||||
|
||||
template <class T>
|
||||
auto make_strict_not_null(T&& t) {
|
||||
return strict_not_null<std::remove_cv_t<std::remove_reference_t<T>>>{std::forward<T>(t)};
|
||||
}
|
||||
|
||||
} // namespace gsl
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <class T>
|
||||
struct hash<gsl::strict_not_null<T>>
|
||||
{
|
||||
std::size_t operator()(const gsl::strict_not_null<T>& value) const { return hash<T>{}(value); }
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
#undef constexpr
|
||||
#pragma pop_macro("constexpr")
|
||||
|
@ -1,130 +0,0 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef GSL_TRANSITION_H
|
||||
#define GSL_TRANSITION_H
|
||||
|
||||
#include <gsl/gsl_assert> // for Ensures, Expects
|
||||
#include <gsl/pointers> // for gsl::not_null
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
#pragma push_macro("constexpr")
|
||||
#define constexpr /*constexpr*/
|
||||
|
||||
#endif // defined(_MSC_VER) && _MSC_VER < 1910
|
||||
|
||||
namespace gsl_helpers
|
||||
{
|
||||
//
|
||||
// sloppy_not_null
|
||||
//
|
||||
// Restricts a pointer or smart pointer to only hold non-null values,
|
||||
//
|
||||
// - provides a sloppy (i.e. no explicit contructor from T) wrapper of gsl::not_null
|
||||
// - is temporary, only to be used to incrementally transition of code
|
||||
// using older version of gsl::not_null to the new one that made the constructor explicit
|
||||
//
|
||||
// To make the transition:
|
||||
//
|
||||
// - replace all occurences of gsl::not_null in your code by sloppy_not_null
|
||||
// variant: rename gsl::not_null by NotNull by including the following in your code,
|
||||
// foe example, in a common include file:
|
||||
//
|
||||
// template<typename T>
|
||||
// using NotNull = gsl::not_null<T>;
|
||||
//
|
||||
// compile using old version of GSL
|
||||
// change GSL version and replace gsl::not_null by gsl_helpers::sloppy_not_null
|
||||
// in the added code lines above
|
||||
//
|
||||
// - compile - compilation should be successful
|
||||
// - replace some sloppy_not_nulls by gsl::not_null, fix compilation erros,
|
||||
// redesign as needed, compile and test
|
||||
// - repeat until no sloppy_not_nulls remain
|
||||
//
|
||||
template <class T>
|
||||
class sloppy_not_null: public gsl::not_null<T>
|
||||
{
|
||||
public:
|
||||
|
||||
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr sloppy_not_null(U&& u) :
|
||||
gsl::not_null<T>(std::forward<U>(u))
|
||||
{}
|
||||
|
||||
template <typename = std::enable_if_t<!std::is_same<std::nullptr_t, T>::value>>
|
||||
constexpr sloppy_not_null(T u) :
|
||||
gsl::not_null<T>(u)
|
||||
{}
|
||||
|
||||
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr sloppy_not_null(const gsl::not_null<U>& other) :
|
||||
gsl::not_null<T>(other)
|
||||
{}
|
||||
|
||||
sloppy_not_null(sloppy_not_null&& other) = default;
|
||||
sloppy_not_null(const sloppy_not_null& other) = default;
|
||||
sloppy_not_null& operator=(const sloppy_not_null& other) = default;
|
||||
sloppy_not_null& operator=(const gsl::not_null<T>& other)
|
||||
{
|
||||
gsl::not_null<T>::operator=(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// prevents compilation when someone attempts to assign a null pointer constant
|
||||
sloppy_not_null(std::nullptr_t) = delete;
|
||||
sloppy_not_null& operator=(std::nullptr_t) = delete;
|
||||
|
||||
// unwanted operators...pointers only point to single objects!
|
||||
sloppy_not_null& operator++() = delete;
|
||||
sloppy_not_null& operator--() = delete;
|
||||
sloppy_not_null operator++(int) = delete;
|
||||
sloppy_not_null operator--(int) = delete;
|
||||
sloppy_not_null& operator+=(std::ptrdiff_t) = delete;
|
||||
sloppy_not_null& operator-=(std::ptrdiff_t) = delete;
|
||||
void operator[](std::ptrdiff_t) const = delete;
|
||||
};
|
||||
|
||||
// more unwanted operators
|
||||
template <class T, class U>
|
||||
std::ptrdiff_t operator-(const sloppy_not_null<T>&, const sloppy_not_null<U>&) = delete;
|
||||
template <class T>
|
||||
sloppy_not_null<T> operator-(const sloppy_not_null<T>&, std::ptrdiff_t) = delete;
|
||||
template <class T>
|
||||
sloppy_not_null<T> operator+(const sloppy_not_null<T>&, std::ptrdiff_t) = delete;
|
||||
template <class T>
|
||||
sloppy_not_null<T> operator+(std::ptrdiff_t, const sloppy_not_null<T>&) = delete;
|
||||
|
||||
} // namespace gsl
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <class T>
|
||||
struct hash<gsl_helpers::sloppy_not_null<T>>
|
||||
{
|
||||
std::size_t operator()(const gsl_helpers::sloppy_not_null<T>& value) const { return hash<T>{}(value); }
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1910
|
||||
#undef constexpr
|
||||
#pragma pop_macro("constexpr")
|
||||
|
||||
#endif // defined(_MSC_VER) && _MSC_VER < 1910
|
||||
|
||||
#endif // GSL_TRANSITION_H
|
||||
|
@ -107,7 +107,7 @@ add_gsl_test(utils_tests)
|
||||
add_gsl_test(owner_tests)
|
||||
add_gsl_test(byte_tests)
|
||||
add_gsl_test(algorithm_tests)
|
||||
add_gsl_test(sloppy_notnull_tests)
|
||||
add_gsl_test(strict_notnull_tests)
|
||||
|
||||
|
||||
# No exception tests
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, TEST_...
|
||||
|
||||
#include <gsl/pointers> // for not_null, operator<, operator<=, operator>
|
||||
#include <gsl/pointers> // for not_null, operator<, operator<=, operator>
|
||||
|
||||
#include <algorithm> // for addressof
|
||||
#include <memory> // for shared_ptr, make_shared, operator<, opera...
|
||||
@ -128,47 +128,105 @@ struct NonCopyableNonMovable
|
||||
};
|
||||
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
bool helper(not_null<int*> p)
|
||||
{
|
||||
return *p == 12;
|
||||
}
|
||||
|
||||
bool helper(not_null<int*> p) { return *p == 12; }
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
bool helper_const(not_null<const int*> p)
|
||||
{
|
||||
return *p == 12;
|
||||
}
|
||||
bool helper_const(not_null<const int*> p) { return *p == 12; }
|
||||
|
||||
int* return_pointer() { return nullptr; }
|
||||
const int* return_pointer_const() { return nullptr; }
|
||||
|
||||
GSL_SUPPRESS(con.4) // NO-FORMAT: attribute
|
||||
TEST_CASE("TestNotNullConstructors")
|
||||
{
|
||||
{
|
||||
#ifdef CONFIRM_COMPILATION_ERRORS
|
||||
not_null<int*> p = nullptr; // yay...does not compile!
|
||||
not_null<std::vector<char>*> p = 0; // yay...does not compile!
|
||||
not_null<int*> p; // yay...does not compile!
|
||||
std::unique_ptr<int> up = std::make_unique<int>(120);
|
||||
not_null<int*> p = up;
|
||||
not_null<int*> p = nullptr; // yay...does not compile!
|
||||
not_null<std::vector<char>*> p1 = 0; // yay...does not compile!
|
||||
not_null<int*> p2; // yay...does not compile!
|
||||
std::unique_ptr<int> up = std::make_unique<int>(120);
|
||||
not_null<int*> p3 = up;
|
||||
|
||||
// Forbid non-nullptr assignable types
|
||||
not_null<std::vector<int>> f(std::vector<int>{1});
|
||||
not_null<int> z(10);
|
||||
not_null<std::vector<int>> y({1, 2});
|
||||
// Forbid non-nullptr assignable types
|
||||
not_null<std::vector<int>> f(std::vector<int>{1});
|
||||
not_null<int> z(10);
|
||||
not_null<std::vector<int>> y({1, 2});
|
||||
#endif
|
||||
int i = 12;
|
||||
auto rp = RefCounted<int>(&i);
|
||||
not_null<int*> p(rp);
|
||||
CHECK(p.get() == &i);
|
||||
}
|
||||
|
||||
not_null<std::shared_ptr<int>> x(
|
||||
std::make_shared<int>(10)); // shared_ptr<int> is nullptr assignable
|
||||
{
|
||||
// from shared pointer
|
||||
int i = 12;
|
||||
auto rp = RefCounted<int>(&i);
|
||||
not_null<int*> p(rp);
|
||||
CHECK(p.get() == &i);
|
||||
|
||||
#ifdef GSL_THROW_ON_CONTRACT_VIOLATION
|
||||
int* pi = nullptr;
|
||||
CHECK_THROWS_AS(not_null<decltype(pi)>(pi), fail_fast);
|
||||
#endif
|
||||
not_null<std::shared_ptr<int>> x(
|
||||
std::make_shared<int>(10)); // shared_ptr<int> is nullptr assignable
|
||||
|
||||
int* pi = nullptr;
|
||||
CHECK_THROWS_AS(not_null<decltype(pi)>(pi), fail_fast);
|
||||
}
|
||||
|
||||
{
|
||||
// from pointer to local
|
||||
int t = 42;
|
||||
|
||||
not_null<int*> x = &t;
|
||||
helper(&t);
|
||||
helper_const(&t);
|
||||
|
||||
CHECK(*x == 42);
|
||||
}
|
||||
|
||||
{
|
||||
// from raw pointer
|
||||
// from not_null pointer
|
||||
|
||||
int t = 42;
|
||||
int* p = &t;
|
||||
|
||||
not_null<int*> x = p;
|
||||
helper(p);
|
||||
helper_const(p);
|
||||
helper(x);
|
||||
helper_const(x);
|
||||
|
||||
CHECK(*x == 42);
|
||||
}
|
||||
|
||||
{
|
||||
// from raw const pointer
|
||||
// from not_null const pointer
|
||||
|
||||
int t = 42;
|
||||
const int* cp = &t;
|
||||
|
||||
not_null<const int*> x = cp;
|
||||
helper_const(cp);
|
||||
helper_const(x);
|
||||
|
||||
CHECK(*x == 42);
|
||||
}
|
||||
|
||||
{
|
||||
// from not_null const pointer, using auto
|
||||
int t = 42;
|
||||
const int* cp = &t;
|
||||
|
||||
auto x = not_null<const int*>{cp};
|
||||
|
||||
CHECK(*x == 42);
|
||||
}
|
||||
|
||||
{
|
||||
// from returned pointer
|
||||
|
||||
CHECK_THROWS_AS(helper(return_pointer()), fail_fast);
|
||||
CHECK_THROWS_AS(helper_const(return_pointer()), fail_fast);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
GSL_SUPPRESS(con.4) // NO-FORMAT: attribute
|
||||
void ostream_helper(T v)
|
||||
{
|
||||
@ -474,5 +532,5 @@ TEST_CASE("TestMakeNotNull")
|
||||
#endif
|
||||
}
|
||||
|
||||
static_assert(std::is_nothrow_move_constructible<not_null<void *>>::value, "not_null must be no-throw move constructible");
|
||||
|
||||
static_assert(std::is_nothrow_move_constructible<not_null<void*>>::value,
|
||||
"not_null must be no-throw move constructible");
|
||||
|
@ -1,124 +0,0 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// blanket turn off warnings from CppCoreCheck from catch
|
||||
// so people aren't annoyed by them when running the tool.
|
||||
#pragma warning(disable : 26440 26426) // from catch
|
||||
|
||||
// Fix VS2015 build breaks in Release
|
||||
#pragma warning(disable : 4702) // unreachable code
|
||||
#endif
|
||||
|
||||
#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, TEST_...
|
||||
|
||||
#include <gsl/pointers> // for not_null, operator<, operator<=, operator>
|
||||
#include <samples/gsl_transition> // for sloppy_not_null
|
||||
|
||||
namespace gsl
|
||||
{
|
||||
struct fail_fast;
|
||||
} // namespace gsl
|
||||
|
||||
using namespace gsl;
|
||||
using namespace gsl_helpers;
|
||||
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
bool helper(not_null<int*> p) { return *p == 12; }
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
bool helper_const(not_null<const int*> p) { return *p == 12; }
|
||||
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
bool sloppy_helper(sloppy_not_null<int*> p) { return *p == 12; }
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
bool sloppy_helper_const(sloppy_not_null<const int*> p) { return *p == 12; }
|
||||
|
||||
TEST_CASE("TestSloppyNotNull")
|
||||
{
|
||||
{
|
||||
// raw ptr <-> sloppy_not_null
|
||||
int x = 42;
|
||||
|
||||
const sloppy_not_null<int*> snn = &x;
|
||||
|
||||
sloppy_helper(&x);
|
||||
sloppy_helper_const(&x);
|
||||
|
||||
CHECK(*snn == 42);
|
||||
}
|
||||
|
||||
{
|
||||
// sloppy_not_null -> sloppy_not_null
|
||||
int x = 42;
|
||||
|
||||
sloppy_not_null<int*> snn1{&x};
|
||||
const sloppy_not_null<int*> snn2{&x};
|
||||
|
||||
sloppy_helper(snn1);
|
||||
sloppy_helper_const(snn1);
|
||||
|
||||
CHECK(snn1 == snn2);
|
||||
}
|
||||
|
||||
{
|
||||
// sloppy_not_null -> not_null
|
||||
int x = 42;
|
||||
|
||||
sloppy_not_null<int*> snn{&x};
|
||||
|
||||
const not_null<int*> nn1 = snn;
|
||||
const not_null<int*> nn2{snn};
|
||||
|
||||
helper(snn);
|
||||
helper_const(snn);
|
||||
|
||||
CHECK(snn == nn1);
|
||||
CHECK(snn == nn2);
|
||||
}
|
||||
|
||||
{
|
||||
// not_null -> sloppy_not_null
|
||||
int x = 42;
|
||||
|
||||
not_null<int*> nn{&x};
|
||||
|
||||
const sloppy_not_null<int*> snn1{nn};
|
||||
const sloppy_not_null<int*> snn2 = nn;
|
||||
|
||||
sloppy_helper(nn);
|
||||
sloppy_helper_const(nn);
|
||||
|
||||
CHECK(snn1 == nn);
|
||||
CHECK(snn2 == nn);
|
||||
|
||||
std::hash<sloppy_not_null<int*>> hash_snn;
|
||||
std::hash<not_null<int*>> hash_nn;
|
||||
|
||||
CHECK(hash_nn(snn1) == hash_nn(nn));
|
||||
CHECK(hash_snn(snn1) == hash_nn(nn));
|
||||
CHECK(hash_nn(snn1) == hash_nn(snn2));
|
||||
CHECK(hash_snn(snn1) == hash_snn(nn));
|
||||
}
|
||||
|
||||
#ifdef CONFIRM_COMPILATION_ERRORS
|
||||
{
|
||||
sloppy_not_null<int*> p{nullptr};
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static_assert(std::is_nothrow_move_constructible<sloppy_not_null<void*>>::value,
|
||||
"sloppy_not_null must be no-throw move constructible");
|
193
tests/strict_notnull_tests.cpp
Normal file
193
tests/strict_notnull_tests.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// blanket turn off warnings from CppCoreCheck from catch
|
||||
// so people aren't annoyed by them when running the tool.
|
||||
#pragma warning(disable : 26440 26426) // from catch
|
||||
|
||||
// Fix VS2015 build breaks in Release
|
||||
#pragma warning(disable : 4702) // unreachable code
|
||||
#endif
|
||||
|
||||
#include <catch/catch.hpp> // for AssertionHandler, StringRef, CHECK, TEST_...
|
||||
#include <gsl/pointers> // for not_null, operator<, operator<=, operator>
|
||||
|
||||
namespace gsl
|
||||
{
|
||||
struct fail_fast;
|
||||
} // namespace gsl
|
||||
|
||||
using namespace gsl;
|
||||
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
bool helper(not_null<int*> p) { return *p == 12; }
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
bool helper_const(not_null<const int*> p) { return *p == 12; }
|
||||
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
bool strict_helper(strict_not_null<int*> p) { return *p == 12; }
|
||||
GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
|
||||
bool strict_helper_const(strict_not_null<const int*> p) { return *p == 12; }
|
||||
|
||||
int* return_pointer() { return nullptr; }
|
||||
const int* return_pointer_const() { return nullptr; }
|
||||
|
||||
GSL_SUPPRESS(con.4) // NO-FORMAT: attribute
|
||||
TEST_CASE("TestStrictNotNull")
|
||||
{
|
||||
{
|
||||
// raw ptr <-> strict_not_null
|
||||
int x = 42;
|
||||
|
||||
#ifdef CONFIRM_COMPILATION_ERRORS
|
||||
strict_not_null<int*> snn = &x;
|
||||
strict_helper(&x);
|
||||
strict_helper_const(&x);
|
||||
strict_helper(return_pointer());
|
||||
strict_helper_const(return_pointer_const());
|
||||
#endif
|
||||
|
||||
const strict_not_null<int*> snn1{&x};
|
||||
|
||||
helper(snn1);
|
||||
helper_const(snn1);
|
||||
|
||||
CHECK(*snn1 == 42);
|
||||
}
|
||||
|
||||
{
|
||||
// strict_not_null -> strict_not_null
|
||||
int x = 42;
|
||||
|
||||
strict_not_null<int*> snn1{&x};
|
||||
const strict_not_null<int*> snn2{&x};
|
||||
|
||||
strict_helper(snn1);
|
||||
strict_helper_const(snn1);
|
||||
strict_helper_const(snn2);
|
||||
|
||||
CHECK(snn1 == snn2);
|
||||
}
|
||||
|
||||
{
|
||||
// strict_not_null -> not_null
|
||||
int x = 42;
|
||||
|
||||
strict_not_null<int*> snn{&x};
|
||||
|
||||
const not_null<int*> nn1 = snn;
|
||||
const not_null<int*> nn2{snn};
|
||||
|
||||
helper(snn);
|
||||
helper_const(snn);
|
||||
|
||||
CHECK(snn == nn1);
|
||||
CHECK(snn == nn2);
|
||||
}
|
||||
|
||||
{
|
||||
// not_null -> strict_not_null
|
||||
int x = 42;
|
||||
|
||||
not_null<int*> nn{&x};
|
||||
|
||||
const strict_not_null<int*> snn1{nn};
|
||||
const strict_not_null<int*> snn2{nn};
|
||||
|
||||
strict_helper(nn);
|
||||
strict_helper_const(nn);
|
||||
|
||||
CHECK(snn1 == nn);
|
||||
CHECK(snn2 == nn);
|
||||
|
||||
std::hash<strict_not_null<int*>> hash_snn;
|
||||
std::hash<not_null<int*>> hash_nn;
|
||||
|
||||
CHECK(hash_nn(snn1) == hash_nn(nn));
|
||||
CHECK(hash_snn(snn1) == hash_nn(nn));
|
||||
CHECK(hash_nn(snn1) == hash_nn(snn2));
|
||||
CHECK(hash_snn(snn1) == hash_snn(nn));
|
||||
}
|
||||
|
||||
#ifdef CONFIRM_COMPILATION_ERRORS
|
||||
{
|
||||
strict_not_null<int*> p{nullptr};
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) && (__cplusplus >= 201703L)
|
||||
|
||||
GSL_SUPPRESS(con.4) // NO-FORMAT: attribute
|
||||
TEST_CASE("TestStrictNotNullConstructorTypeDeduction")
|
||||
{
|
||||
{
|
||||
int i = 42;
|
||||
|
||||
strict_not_null x{&i};
|
||||
helper(strict_not_null{&i});
|
||||
helper_const(strict_not_null{&i});
|
||||
|
||||
CHECK(*x == 42);
|
||||
}
|
||||
|
||||
{
|
||||
int i = 42;
|
||||
int* p = &i;
|
||||
|
||||
strict_not_null x{p};
|
||||
helper(strict_not_null{p});
|
||||
helper_const(strict_not_null{p});
|
||||
|
||||
CHECK(*x == 42);
|
||||
}
|
||||
|
||||
{
|
||||
auto workaround_macro = []() {
|
||||
int* p1 = nullptr;
|
||||
const strict_not_null x{p1};
|
||||
};
|
||||
CHECK_THROWS_AS(workaround_macro(), fail_fast);
|
||||
}
|
||||
|
||||
{
|
||||
auto workaround_macro = []() {
|
||||
const int* p1 = nullptr;
|
||||
const strict_not_null x{p1};
|
||||
};
|
||||
CHECK_THROWS_AS(workaround_macro(), fail_fast);
|
||||
}
|
||||
|
||||
{
|
||||
int* p = nullptr;
|
||||
|
||||
CHECK_THROWS_AS(helper(strict_not_null{p}), fail_fast);
|
||||
CHECK_THROWS_AS(helper_const(strict_not_null{p}), fail_fast);
|
||||
}
|
||||
|
||||
#ifdef CONFIRM_COMPILATION_ERRORS
|
||||
{
|
||||
strict_not_null x{nullptr};
|
||||
helper(strict_not_null{nullptr});
|
||||
helper_const(strict_not_null{nullptr});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif // #if defined(__cplusplus) && (__cplusplus >= 201703L)
|
||||
|
||||
static_assert(std::is_nothrow_move_constructible<strict_not_null<void*>>::value,
|
||||
"strict_not_null must be no-throw move constructible");
|
@ -20,7 +20,8 @@
|
||||
|
||||
// blanket turn off warnings from CppCoreCheck from catch
|
||||
// so people aren't annoyed by them when running the tool.
|
||||
#pragma warning(disable : 26401 26409 26415 26418 26426 26429 26432 26433 26434 26435 26436 26439 26440 26443 26444 26446 26447 26451 26460 26461 26466 26472 26481 26482 26485 26492 26493 26494 26495 26496 26497) // from catch
|
||||
#include <CodeAnalysis/Warnings.h>
|
||||
#pragma warning(disable : ALL_CODE_ANALYSIS_WARNINGS) // from catch
|
||||
#endif // _MSC_VER
|
||||
|
||||
#include <catch/catch.hpp>
|
||||
|
Loading…
Reference in New Issue
Block a user