Merge branch 'master' into fixmaybenull

Conflicts:
	include/gsl.h
	tests/maybenull_tests.cpp
This commit is contained in:
Treb Connell 2015-09-28 14:56:08 -07:00
commit 27ff004e84
8 changed files with 425 additions and 293 deletions

View File

@ -26,6 +26,7 @@ The test suite that exercises GSL has been built and passes successfully on the
* GNU/Linux using GCC 5.1 * GNU/Linux using GCC 5.1
* OS X Yosemite using Xcode with AppleClang 7.0.0.7000072 * OS X Yosemite using Xcode with AppleClang 7.0.0.7000072
* OS X Yosemite using GCC-5.2.0 * OS X Yosemite using GCC-5.2.0
* FreeBSD 10.x with Clang/LLVM 3.6
> If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider > If you successfully port GSL to another platform, we would love to hear from you. Please submit an issue to let us know. Also please consider
contributing any changes that were necessary back to this project to benefit the wider community. contributing any changes that were necessary back to this project to benefit the wider community.

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,9 @@
#pragma once #pragma once
#ifndef GSL_FAIL_FAST_H
#define GSL_FAIL_FAST_H
#include <exception> #include <exception>
#include <stdexcept> #include <stdexcept>
@ -45,3 +48,5 @@ inline void fail_fast_assert(bool cond, const char* const) { if (!cond) std::ter
#endif // SAFER_CPP_TESTING #endif // SAFER_CPP_TESTING
} }
#endif // GSL_FAIL_FAST_H

View File

@ -16,6 +16,9 @@
#pragma once #pragma once
#ifndef GSL_GSL_H
#define GSL_GSL_H
#include "array_view.h" // array_view, strided_array_view... #include "array_view.h" // array_view, strided_array_view...
#include "string_view.h" // zstring, string_view, zstring_builder... #include "string_view.h" // zstring, string_view, zstring_builder...
#include <memory> #include <memory>
@ -47,7 +50,7 @@ template <class F>
class Final_act class Final_act
{ {
public: public:
explicit Final_act(F f) : f_(f) {} explicit Final_act(F f) : f_(std::move(f)) {}
Final_act(const Final_act&& other) : f_(other.f_) {} Final_act(const Final_act&& other) : f_(other.f_) {}
Final_act(const Final_act&) = delete; Final_act(const Final_act&) = delete;
@ -61,7 +64,10 @@ private:
// finally() - convenience function to generate a Final_act // finally() - convenience function to generate a Final_act
template <class F> template <class F>
Final_act<F> finally(F f) { return Final_act<F>(f); } Final_act<F> finally(const F &f) { return Final_act<F>(f); }
template <class F>
Final_act<F> finally(F &&f) { return Final_act<F>(std::forward<F>(f)); }
// narrow_cast(): a searchable way to do narrowing casts of values // narrow_cast(): a searchable way to do narrowing casts of values
template<class T, class U> template<class T, class U>
@ -102,23 +108,30 @@ typename Cont::value_type& at(Cont& cont, size_t index) { fail_fast_assert(index
template<class T> template<class T>
class not_null class not_null
{ {
static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
public: public:
not_null(T t) : ptr_(t) { ensure_invariant(); } not_null(T t) : ptr_(t) { ensure_invariant(); }
not_null& operator=(const T& t) { ptr_ = t; ensure_invariant(); return *this; }
// deleting these two prevents compilation when initialized with a nullptr or literal 0
not_null(std::nullptr_t) = delete;
not_null(int) = delete;
not_null(const not_null &other) = default; not_null(const not_null &other) = default;
not_null& operator=(const not_null &other) = default;
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>> template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
not_null(const not_null<U> &other) : ptr_(other.get()) not_null(const not_null<U> &other)
{ {
*this = other;
} }
not_null<T>& operator=(const T& t) { ptr_ = t; ensure_invariant(); return *this; } template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
not_null& operator=(const not_null<U> &other)
{
ptr_ = other.get();
return *this;
}
// prevents compilation when someone attempts to assign a nullptr // prevents compilation when someone attempts to assign a nullptr
not_null(std::nullptr_t) = delete;
not_null(int) = delete;
not_null<T>& operator=(std::nullptr_t) = delete; not_null<T>& operator=(std::nullptr_t) = delete;
not_null<T>& operator=(int) = delete; not_null<T>& operator=(int) = delete;
@ -159,29 +172,21 @@ private:
// //
// Describes an optional pointer - provides symmetry with not_null // Describes an optional pointer - provides symmetry with not_null
// //
template<class T>
class maybe_null_ret;
template<class T> template<class T>
class maybe_null_dbg class maybe_null_dbg
{ {
template<class U> template<class U>
friend class maybe_null_dbg; friend class maybe_null_dbg;
public:
static_assert(std::is_constructible<T, std::nullptr_t>::value, "maybe_null's template parameter must be constructible from nullptr");
static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
public:
maybe_null_dbg() : ptr_(nullptr), tested_(false) {} maybe_null_dbg() : ptr_(nullptr), tested_(false) {}
maybe_null_dbg(std::nullptr_t) : ptr_(nullptr), tested_(false) {}
maybe_null_dbg(const T& p) : ptr_(p), tested_(false) {} maybe_null_dbg(const T& p) : ptr_(p), tested_(false) {}
maybe_null_dbg(const maybe_null_dbg& rhs) : ptr_(rhs.ptr_), tested_(false) {}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_dbg(const not_null<U> &other) : ptr_(other.get()), tested_(false)
{
}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_dbg(const maybe_null_dbg<U> &other) : ptr_(other.get()), tested_(false)
{
}
maybe_null_dbg& operator=(const T& p) maybe_null_dbg& operator=(const T& p)
{ {
if (ptr_ != p) if (ptr_ != p)
@ -192,6 +197,8 @@ public:
return *this; return *this;
} }
maybe_null_dbg(const maybe_null_dbg& rhs) : ptr_(rhs.ptr_), tested_(false) {}
maybe_null_dbg& operator=(const maybe_null_dbg& rhs) maybe_null_dbg& operator=(const maybe_null_dbg& rhs)
{ {
if (this != &rhs) if (this != &rhs)
@ -202,6 +209,43 @@ public:
return *this; return *this;
} }
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_dbg(const not_null<U> &other) : ptr_(other.get()), tested_(false) {}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_dbg& operator=(const not_null<U> &other)
{
ptr_ = other.get();
tested_ = false;
return *this;
}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_dbg(const maybe_null_dbg<U> &other) : ptr_(other.get()), tested_(false) {}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_dbg& operator=(const maybe_null_dbg<U> &other)
{
ptr_ = other.get();
tested_ = false;
return *this;
}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_dbg(const maybe_null_ret<U> &other) : ptr_(other.get()), tested_(false) {}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_dbg& operator=(const maybe_null_ret<U> &other)
{
ptr_ = other.get();
tested_ = false;
return *this;
}
bool present() const { tested_ = true; return ptr_ != nullptr; } bool present() const { tested_ = true; return ptr_ != nullptr; }
bool operator==(const T& rhs) const { tested_ = true; return ptr_ == rhs; } bool operator==(const T& rhs) const { tested_ = true; return ptr_ == rhs; }
@ -241,34 +285,49 @@ private:
template<class T> template<class T>
class maybe_null_ret class maybe_null_ret
{ {
static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
public: public:
static_assert(std::is_constructible<T, std::nullptr_t>::value, "maybe_null's template parameter must be constructible from nullptr");
maybe_null_ret() : ptr_(nullptr) {} maybe_null_ret() : ptr_(nullptr) {}
maybe_null_ret(std::nullptr_t) : ptr_(nullptr) {} maybe_null_ret(std::nullptr_t) : ptr_(nullptr) {}
maybe_null_ret(const T& p) : ptr_(p) {} maybe_null_ret(const T& p) : ptr_(p) {}
maybe_null_ret& operator=(const T& p) { ptr_ = p; return *this; }
maybe_null_ret(const maybe_null_ret& rhs) = default; maybe_null_ret(const maybe_null_ret& rhs) = default;
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_ret(const not_null<U> &other) : ptr_(other.get())
{
}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_ret(const maybe_null_ret<U> &other) : ptr_(other.get())
{
}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_ret(const maybe_null_dbg<U> &other) : ptr_(other.get())
{
}
maybe_null_ret& operator=(const T& p) { if (ptr_ != p) { ptr_ = p; } return *this; }
maybe_null_ret& operator=(const maybe_null_ret& rhs) = default; maybe_null_ret& operator=(const maybe_null_ret& rhs) = default;
bool operator==(const T& rhs) const { return ptr_ == rhs; } template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
bool operator!=(const T& rhs) const { return ptr_ != rhs; } maybe_null_ret(const not_null<U> &other) : ptr_(other.get()) {}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_ret& operator=(const not_null<U> &other)
{
ptr_ = other.get();
return *this;
}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_ret(const maybe_null_ret<U> &other) : ptr_(other.get()) {}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_ret& operator=(const maybe_null_ret<U> &other)
{
ptr_ = other.get();
return *this;
}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_ret(const maybe_null_dbg<U> &other) : ptr_(other.get()) {}
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
maybe_null_ret& operator=(const maybe_null_dbg<U> &other)
{
ptr_ = other.get();
return *this;
}
bool present() const { return ptr_ != nullptr; } bool present() const { return ptr_ != nullptr; }
@ -295,3 +354,5 @@ private:
template<class T> using maybe_null = maybe_null_ret<T>; template<class T> using maybe_null = maybe_null_ret<T>;
} // namespace Guide } // namespace Guide
#endif // GSL_GSL_H

View File

@ -16,6 +16,9 @@
#pragma once #pragma once
#ifndef GSL_STRING_VIEW_H
#define GSL_STRING_VIEW_H
#include "array_view.h" #include "array_view.h"
#include <cstring> #include <cstring>
@ -176,3 +179,5 @@ using zstring_builder = basic_zstring_builder<char, Max>;
template <size_t Max = dynamic_range> template <size_t Max = dynamic_range>
using wzstring_builder = basic_zstring_builder<wchar_t, Max>; using wzstring_builder = basic_zstring_builder<wchar_t, Max>;
} }
#endif // GSL_STRING_VIEW_H

View File

@ -16,6 +16,8 @@
#include <UnitTest++/UnitTest++.h> #include <UnitTest++/UnitTest++.h>
#include <gsl.h> #include <gsl.h>
#include <vector>
#include <iostream>
using namespace Guide; using namespace Guide;
@ -27,12 +29,24 @@ SUITE(MaybeNullTests)
{ {
TEST(TestMaybeNull1) TEST(TestMaybeNull1)
{ {
#ifdef CONFIRM_COMPILATION_ERRORS
// Forbid non-nullptr assignable types
maybe_null_ret<std::vector<int>> f_ret(std::vector<int>{1});
maybe_null_ret<std::vector<int>> f_ret(std::vector<int>{1});
maybe_null_ret<int> z_ret(10);
maybe_null_dbg<std::vector<int>> y_dbg({1,2});
maybe_null_dbg<int> z_dbg(10);
maybe_null_dbg<std::vector<int>> y_dbg({1,2});
#endif
int n = 5; int n = 5;
maybe_null_dbg<int *> opt_n(&n); maybe_null_dbg<int *> opt_n(&n);
int result = 0; int result = 0;
bool threw = false; bool threw = false;
CHECK_THROW(result = *opt_n, fail_fast); CHECK_THROW(result = *opt_n, fail_fast);
maybe_null_ret<std::shared_ptr<int>> x_ret(std::make_shared<int>(10)); // shared_ptr<int> is nullptr assignable
maybe_null_dbg<std::shared_ptr<int>> x_dbg(std::make_shared<int>(10)); // shared_ptr<int> is nullptr assignable
} }
TEST(TestMaybeNull2) TEST(TestMaybeNull2)
@ -242,18 +256,41 @@ SUITE(MaybeNullTests)
CHECK(p1.get() != nullptr); CHECK(p1.get() != nullptr);
} }
TEST(TestMaybeNullPtrT) TEST(TestMaybeNullAssignmentOps)
{ {
maybe_null<std::nullptr_t> p1; MyBase base;
maybe_null<std::nullptr_t> p2; MyDerived derived;
Unrelated unrelated;
CHECK_THROW(p1.get(), fail_fast); not_null<MyBase*> nnBase(&base);
not_null<MyDerived*> nnDerived(&derived);
not_null<Unrelated*> nnUnrelated(&unrelated);
CHECK(p1 == p2); maybe_null_ret<MyBase*> mnBase_ret1(&base), mnBase_ret2;
mnBase_ret2 = mnBase_ret1; // maybe_null_ret<T> = maybe_null_ret<T>
mnBase_ret2 = nnBase; // maybe_null_ret<T> = not_null<T>
// Make sure we no longer throw here maybe_null_ret<MyDerived*> mnDerived_ret(&derived);
CHECK(p1.get() == nullptr); mnBase_ret2 = mnDerived_ret; // maybe_null_ret<T> = maybe_null_ret<U>
CHECK(p2.get() == nullptr); mnBase_ret1 = &derived; // maybe_null_ret<T> = U;
mnBase_ret1 = nnDerived; // maybe_null_ret<T> = not_null<U>
maybe_null_ret<Unrelated*> mnUnrelated_ret;
mnUnrelated_ret = &unrelated; // maybe_null_ret<T> = T
maybe_null_dbg<MyBase*> mnBase_dbg1(&base), mnBase_dbg2;
mnBase_dbg2 = mnBase_dbg1; // maybe_null_dbg<T> = maybe_null_dbg<T>
mnBase_dbg2 = nnBase; // maybe_null_dbg<T> = not_null<T>
maybe_null_dbg<MyDerived*> mnDerived_dbg(&derived);
CHECK(mnDerived_dbg.present());
mnBase_dbg2 = mnDerived_dbg; // maybe_null_dbg<T> = maybe_null_dbg<U>
mnBase_dbg1 = &derived; // maybe_null_dbg<T> = U;
mnBase_dbg1 = nnDerived; // maybe_null_dbg<T> = not_null<U>
maybe_null_dbg<Unrelated*> mnUnrelated_dbg;
mnUnrelated_dbg = &unrelated; // maybe_null_dbg<T> = T
} }
} }

View File

@ -16,6 +16,7 @@
#include <UnitTest++/UnitTest++.h> #include <UnitTest++/UnitTest++.h>
#include <gsl.h> #include <gsl.h>
#include <vector>
using namespace Guide; using namespace Guide;
@ -48,21 +49,35 @@ SUITE(NotNullTests)
not_null<int*> p; // yay...does not compile! not_null<int*> p; // yay...does not compile!
std::unique_ptr<int> up = std::make_unique<int>(120); std::unique_ptr<int> up = std::make_unique<int>(120);
not_null<int*> p = up; not_null<int*> p = 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});
#endif #endif
int i = 12; int i = 12;
auto rp = RefCounted<int>(&i); auto rp = RefCounted<int>(&i);
not_null<int*> p(rp); not_null<int*> p(rp);
CHECK(p.get() == &i); CHECK(p.get() == &i);
not_null<std::shared_ptr<int>> x(std::make_shared<int>(10)); // shared_ptr<int> is nullptr assignable
} }
TEST(TestNotNullCasting) TEST(TestNotNullCasting)
{ {
MyBase base;
MyDerived derived; MyDerived derived;
Unrelated unrelated;
not_null<Unrelated*> u = &unrelated;
not_null<MyDerived*> p = &derived; not_null<MyDerived*> p = &derived;
not_null<MyBase*> q = p; not_null<MyBase*> q = &base;
q = p; // allowed with heterogeneous copy ctor
CHECK(q == p); CHECK(q == p);
#ifdef CONFIRM_COMPILATION_ERRORS #ifdef CONFIRM_COMPILATION_ERRORS
q = u; // no viable conversion possible between MyBase* and Unrelated*
p = q; // not possible to implicitly convert MyBase* to MyDerived*
not_null<Unrelated*> r = p; not_null<Unrelated*> r = p;
not_null<Unrelated*> s = reinterpret_cast<Unrelated*>(p); not_null<Unrelated*> s = reinterpret_cast<Unrelated*>(p);
#endif #endif

View File

@ -33,6 +33,7 @@ SUITE(owner_tests)
CHECK(*p == 120); CHECK(*p == 120);
f(p); f(p);
CHECK(*p == 121); CHECK(*p == 121);
delete p;
} }
} }