From ed102549f9264f782b3d33159c1368420ef20634 Mon Sep 17 00:00:00 2001
From: Sam Yates <halfflat@gmail.com>
Date: Fri, 10 Jun 2016 15:11:46 +0200
Subject: [PATCH] Closer integration of optional type.

* Move optionalm headers into src/util
* Change namespace for optional classes to nest::mc::util
* Include unit tests for optional and uninitialized classes
* Simplify (dedoxygenate) comments in uninitialized.hpp
---
 external/optionalm/README                     |   1 -
 src/event_queue.hpp                           |   6 +-
 src/fvm.hpp                                   |   2 -
 .../optionalm.h => src/util/optional.hpp      |  27 +-
 .../util/uninitialized.hpp                    | 110 ++----
 tests/CMakeLists.txt                          |   2 +
 tests/test_optional.cpp                       | 335 ++++++++++++++++++
 tests/test_uninitialized.cpp                  | 200 +++++++++++
 8 files changed, 584 insertions(+), 99 deletions(-)
 delete mode 100644 external/optionalm/README
 rename external/optionalm/optionalm.h => src/util/optional.hpp (94%)
 rename external/optionalm/uninitialized.h => src/util/uninitialized.hpp (50%)
 create mode 100644 tests/test_optional.cpp
 create mode 100644 tests/test_uninitialized.cpp

diff --git a/external/optionalm/README b/external/optionalm/README
deleted file mode 100644
index 206f5968..00000000
--- a/external/optionalm/README
+++ /dev/null
@@ -1 +0,0 @@
-Adapted from github.com:halfflat/optionalm
diff --git a/src/event_queue.hpp b/src/event_queue.hpp
index 80b14a89..989e3487 100644
--- a/src/event_queue.hpp
+++ b/src/event_queue.hpp
@@ -4,7 +4,7 @@
 #include <ostream>
 #include <queue>
 
-#include <optionalm/optionalm.h>
+#include "util/optional.hpp"
 
 namespace nest {
 namespace mc {
@@ -46,14 +46,14 @@ public :
     }
 
     // pop until
-    hf::optionalm::optional<local_event> pop_if_before(float t_until) {
+    util::optional<local_event> pop_if_before(float t_until) {
          if (!queue_.empty() && queue_.top().time < t_until) {
              auto ev = queue_.top();
              queue_.pop();
              return ev;
          }
          else {
-             return hf::optionalm::nothing;
+             return util::nothing;
          }
     }
 
diff --git a/src/fvm.hpp b/src/fvm.hpp
index 49d0af7a..603196c0 100644
--- a/src/fvm.hpp
+++ b/src/fvm.hpp
@@ -19,8 +19,6 @@
 #include <util.hpp>
 
 #include <vector/include/Vector.hpp>
-#include <optionalm/optionalm.h>
-
 #include <mechanisms/expsyn.hpp>
 
 namespace nest {
diff --git a/external/optionalm/optionalm.h b/src/util/optional.hpp
similarity index 94%
rename from external/optionalm/optionalm.h
rename to src/util/optional.hpp
index d22c8ea2..7dd65a27 100644
--- a/external/optionalm/optionalm.h
+++ b/src/util/optional.hpp
@@ -1,4 +1,4 @@
-/*! \file optionalm.h
+/*! \file optional.h
  *  \brief An option class with a monadic interface.
  *
  *  The std::option<T> class was proposed for inclusion into C++14, but was
@@ -14,20 +14,18 @@
  *  is the lack of constexpr versions of the methods and constructors.
  */
 
-#ifndef HF_OPTIONALM_H_
-#define HF_OPTIONALM_H_
+#ifndef UTIL_OPTIONAL_H_
+#define UTIL_OPTIONAL_H_
 
 #include <type_traits>
 #include <stdexcept>
 #include <utility>
 
-#include <optionalm/uninitialized.h>
+#include "util/uninitialized.hpp"
 
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wlogical-op-parentheses"
-
-namespace hf {
-namespace optionalm {
+namespace nest {
+namespace mc {
+namespace util {
 
 template <typename X> struct optional;
 
@@ -64,7 +62,7 @@ namespace detail {
         template <typename Y> friend struct optional;
 
     protected:
-        typedef hf::optionalm::uninitialized<X> D;
+        typedef util::uninitialized<X> D;
 
     public:
         typedef typename D::reference_type reference_type;
@@ -281,7 +279,7 @@ struct optional<void>: detail::optional_base<void> {
     bool operator==(const Y &y) const { return false; }
 
     bool operator==(const optional<void> &o) const {
-        return set && o.set || !set && !o.set;
+        return (set && o.set) || (!set && !o.set);
     }
 };
 
@@ -292,7 +290,6 @@ typename std::enable_if<
     optional<typename std::common_type<typename detail::wrapped_type<A>::type,typename detail::wrapped_type<B>::type>::type>
 >::type
 operator|(A &&a,B &&b) {
-    typedef typename std::common_type<typename detail::wrapped_type<A>::type,typename detail::wrapped_type<B>::type>::type common;
     return a?a:b;
 }
 
@@ -311,8 +308,6 @@ inline optional<void> provided(bool condition) { return condition?optional<void>
 template <typename X>
 optional<X> just(X &&x) { return optional<X>(std::forward<X>(x)); }
 
-}} // namespace hf::optionalm
-
-#pragma clang diagnostic pop
+}}} // namespace nest::mc::util
 
-#endif // ndef HF_OPTIONALM_H_
+#endif // ndef UTIL_OPTIONALM_H_
diff --git a/external/optionalm/uninitialized.h b/src/util/uninitialized.hpp
similarity index 50%
rename from external/optionalm/uninitialized.h
rename to src/util/uninitialized.hpp
index 264e3844..17ab7d38 100644
--- a/external/optionalm/uninitialized.h
+++ b/src/util/uninitialized.hpp
@@ -1,34 +1,21 @@
-/*! \file uninitialized.h
- *  \brief Represent a possibly-uninitialized value, reference or void.
+/* Represent a possibly-uninitialized value, reference or void.
  *
- *  The \c uninitialized\<X\> structure holds space for an item of
- *  type \c X, leaving its construction or destruction to the user.
+ * The uninitialized<X> structure holds space for an item of
+ * type X, leaving its construction or destruction to the user.
+ * 
+ * Specialisations for reference types X & and for the void type
+ * allow for the handling of non-value types in a uniform manner.
  */
 
-#ifndef HF_UNINITIALIZED_H_
-#define HF_UNINITIALIZED_H_
-
-namespace hf {
-namespace optionalm {
+#ifndef UTIL_UNINITIALIZED_H_
+#define UTIL_UNINITIALIZED_H_
 
-/*! \defgroup uninitialized The uninitialized<X> classes.
- *  \{
- * 
- *  The \c uninitialized\<X\> structure holds space for an item of
- *  type \c X, which can then be explicitly constructed or
- *  deconstructed through the \c construct() and \c destruct()
- *  member functions.
- *
- *  Specialisations for reference types <tt>X &</tt> and for the
- *  \c void type allow handling non-value types in a uniform
- *  manner.
- */
+namespace nest {
+namespace mc {
+namespace util {
 
-/*! \brief Maintains storage for a value of type X, with explicit
- *  construction and destruction.
- *
- *  \tparam X
- *      The wrapped value type.
+/* Maintains storage for a value of type X, with explicit
+ * construction and destruction.
  */
 template <typename X>
 struct uninitialized {
@@ -36,49 +23,40 @@ private:
     typename std::aligned_storage<sizeof(X),alignof(X)>::type data;
 
 public:
-    //! &nbsp;
     typedef X *pointer_type;
-    //! &nbsp;
     typedef const X *const_pointer_type;
-    //! &nbsp;
     typedef X &reference_type;
-    //! &nbsp;
     typedef const X &const_reference_type;
 
-    //! Return a pointer to the value.
     pointer_type ptr() { return reinterpret_cast<X *>(&data); }
-    //! Return a const pointer to the value.
     const_pointer_type cptr() const { return reinterpret_cast<const X *>(&data); }
 
-    //! Return a reference to the value.
     reference_type ref() { return *reinterpret_cast<X *>(&data); }
-    //! Return a const reference to the value.
     const_reference_type cref() const { return *reinterpret_cast<const X *>(&data); }
 
-    //! Copy construct the value.
-    template <typename Y=X,typename =typename std::enable_if<std::is_copy_constructible<Y>::value>::type>
+    // Copy construct the value.
+    template <typename Y=X,
+              typename =typename std::enable_if<std::is_copy_constructible<Y>::value>::type>
     void construct(const X &x) { new(&data) X(x); }
 
-    //! General constructor
-    template <typename... Y,typename =typename std::enable_if<std::is_constructible<X,Y...>::value>::type>
+    // General constructor for X, forwarding arguments.
+    template <typename... Y,
+              typename =typename std::enable_if<std::is_constructible<X,Y...>::value>::type>
     void construct(Y&& ...args) { new(&data) X(std::forward<Y>(args)...); }
 
-    //! Call the destructor of the value.
     void destruct() { ptr()->~X(); }
 
-    //! Apply the one-parameter functor F to the value by reference.
+    // Apply the one-parameter functor F to the value by reference.
     template <typename F>
     typename std::result_of<F(reference_type)>::type apply(F &&f) { return f(ref()); }
-    //! Apply the one-parameter functor F to the value by const reference.
+
+    // Apply the one-parameter functor F to the value by const reference.
     template <typename F>
     typename std::result_of<F(const_reference_type)>::type apply(F &&f) const { return f(cref()); }
 };
 
-/*! \brief Maintains storage for a pointer of type X, representing
- *  a possibly uninitialized reference.
- *
- *  \tparam X& 
- *      Wrapped reference type.
+/* Maintains storage for a pointer of type X, representing
+ * a possibly uninitialized reference.
  */
 template <typename X>
 struct uninitialized<X&> {
@@ -86,71 +64,51 @@ private:
     X *data;
 
 public:
-    //! &nbsp;
     typedef X *pointer_type;
-    //! &nbsp;
     typedef const X *const_pointer_type;
-    //! &nbsp;
     typedef X &reference_type;
-    //! &nbsp;
     typedef const X &const_reference_type;
 
-    //! Return a pointer to the value.
     pointer_type ptr() { return data; }
-    //! Return a const pointer to the value.
     const_pointer_type cptr() const { return data; }
 
-    //! Return a reference to the value.
     reference_type ref() { return *data; }
-    //! Return a const reference to the value.
     const_reference_type cref() const { return *data; }
 
-    //! Set the reference data.
     void construct(X &x) { data=&x; }
-    //! Destruct is a NOP for reference data.
     void destruct() {}
 
-    //! Apply the one-parameter functor F to the value by reference.
+    // Apply the one-parameter functor F to the value by reference.
     template <typename F>
     typename std::result_of<F(reference_type)>::type apply(F &&f) { return f(ref()); }
-    //! Apply the one-parameter functor F to the value by const reference.
+    // Apply the one-parameter functor F to the value by const reference.
     template <typename F>
     typename std::result_of<F(const_reference_type)>::type apply(F &&f) const { return f(cref()); }
 };
 
-/*! \brief Wrap a void type in an uninitialized template.
+/* Wrap a void type in an uninitialized template.
  * 
- *  Allows the use of <tt>uninitialized\<X\></tt> for void \c X, for generic
- *  applications.
+ * Allows the use of uninitialized<X> for void X, for generic applications.
  */
-
 template <>
 struct uninitialized<void> {
-    //! &nbsp;
     typedef void *pointer_type;
-    //! &nbsp;
     typedef const void *const_pointer_type;
-    //! &nbsp;
     typedef void reference_type;
-    //! &nbsp;
     typedef void const_reference_type;
 
-    //! &nbsp;
     pointer_type ptr() { return nullptr; }
-    //! &nbsp;
     const_pointer_type cptr() const { return nullptr; }
 
-    //! &nbsp;
     reference_type ref() {}
-    //! &nbsp;
     const_reference_type cref() const {}
 
-    //! No operation.
+    // No operation.
     void construct(...) {}
-    //! No operation.
+    // No operation.
     void destruct() {}
 
-    //! Equivalent to <tt>f()</tt>
+    // Equivalent to f()
     template <typename F>
     typename std::result_of<F()>::type apply(F &&f) const { return f(); }
 };
@@ -167,9 +125,7 @@ struct uninitialized_can_construct<X &,Y>: std::integral_constant<bool,std::is_c
 template <typename... Y>
 struct uninitialized_can_construct<void,Y...>: std::true_type {};
 
-/*! \} */
-
-}} // namespace hf::optionalm
+}}} // namespace nest::mc::util
 
-#endif // ndef HF_UNINITIALIZED_H_
+#endif // ndef UTIL_UNINITIALIZED_H_
 
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 9b612200..33e44398 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -20,6 +20,7 @@ set(TEST_SOURCES
     test_fvm.cpp
     test_matrix.cpp
     test_mechanisms.cpp
+    test_optional.cpp
     test_parameters.cpp
     test_point.cpp
     test_segment.cpp
@@ -27,6 +28,7 @@ set(TEST_SOURCES
     test_swcio.cpp
     test_synapses.cpp
     test_tree.cpp
+    test_uninitialized.cpp
 
     # unit test driver
     test.cpp
diff --git a/tests/test_optional.cpp b/tests/test_optional.cpp
new file mode 100644
index 00000000..e3ca5ecb
--- /dev/null
+++ b/tests/test_optional.cpp
@@ -0,0 +1,335 @@
+#include <typeinfo>
+#include <array>
+#include <algorithm>
+
+#include "gtest.h"
+#include "util/optional.hpp"
+
+using namespace nest::mc::util;
+
+TEST(optionalm,ctors) {
+    optional<int> a,b(3),c=b,d=4;
+
+    ASSERT_FALSE((bool)a);
+    ASSERT_TRUE((bool)b);
+    ASSERT_TRUE((bool)c);
+    ASSERT_TRUE((bool)d);
+
+    EXPECT_EQ(3,b.get());
+    EXPECT_EQ(3,c.get());
+    EXPECT_EQ(4,d.get());
+}
+
+TEST(optionalm,unset_throw) {
+    optional<int> a;
+    int check=10;
+
+    try { a.get(); }
+    catch(optional_unset_error &e) {
+        ++check;
+    }
+    EXPECT_EQ(11,check);
+
+    check=20;
+    a=2;
+    try { a.get(); }
+    catch(optional_unset_error &e) {
+        ++check;
+    }
+    EXPECT_EQ(20,check);
+
+    check=30;
+    a.reset();
+    try { a.get(); }
+    catch(optional_unset_error &e) {
+        ++check;
+    }
+    EXPECT_EQ(31,check);
+}
+
+TEST(optionalm,deref) {
+    struct foo {
+        int a;
+        explicit foo(int a_): a(a_) {}
+        double value() { return 3.0*a; }
+    };
+    
+    optional<foo> f=foo(2);
+    EXPECT_EQ(6.0,f->value());
+    EXPECT_EQ(2,(*f).a);
+}
+
+TEST(optionalm,ctor_conv) {
+    optional<std::array<int,3>> x{{1,2,3}};
+    EXPECT_EQ(3,x->size());
+}
+
+TEST(optionalm,ctor_ref) {
+    int v=10;
+    optional<int &> a(v);
+
+    EXPECT_EQ(10,a.get());
+    v=20;
+    EXPECT_EQ(20,a.get());
+
+    optional<int &> b(a),c=b,d=v;
+    EXPECT_EQ(&(a.get()),&(b.get()));
+    EXPECT_EQ(&(a.get()),&(c.get()));
+    EXPECT_EQ(&(a.get()),&(d.get()));
+}
+
+TEST(optionalm,assign_returns) {
+    optional<int> a=3;
+
+    auto b=(a=4);
+    EXPECT_EQ(typeid(optional<int>),typeid(b));
+
+    auto bp=&(a=4);
+    EXPECT_EQ(&a,bp);
+}
+
+namespace {
+    struct nomove {
+        int value;
+
+        nomove(): value(0) {}
+        nomove(int i): value(i) {}
+        nomove(const nomove &n): value(n.value) {}
+        nomove(nomove &&n) = delete;
+
+        nomove &operator=(const nomove &n) { value=n.value; return *this; }
+
+        bool operator==(const nomove &them) const { return them.value==value; }
+        bool operator!=(const nomove &them) const { return !(*this==them); }
+    };
+}
+ 
+TEST(optionalm,ctor_nomove) {
+    optional<nomove> a(nomove(3));
+    EXPECT_EQ(nomove(3),a.get());
+
+    optional<nomove> b;
+    b=a;
+    EXPECT_EQ(nomove(3),b.get());
+
+    b=optional<nomove>(nomove(4));
+    EXPECT_EQ(nomove(4),b.get());
+}
+
+namespace {
+    struct nocopy {
+        int value;
+
+        nocopy(): value(0) {}
+        nocopy(int i): value(i) {}
+        nocopy(const nocopy &n) = delete;
+        nocopy(nocopy &&n) {
+            value=n.value;
+            n.value=0;
+        }
+
+        nocopy &operator=(const nocopy &n) = delete;
+        nocopy &operator=(nocopy &&n) {
+            value=n.value;
+            n.value=-1;
+            return *this;
+        }
+
+        bool operator==(const nocopy &them) const { return them.value==value; }
+        bool operator!=(const nocopy &them) const { return !(*this==them); }
+    };
+}
+    
+TEST(optionalm,ctor_nocopy) {
+    optional<nocopy> a(nocopy(5));
+    EXPECT_EQ(nocopy(5),a.get());
+
+    optional<nocopy> b(std::move(a));
+    EXPECT_EQ(nocopy(5),b.get());
+    EXPECT_EQ(0,a.get().value);
+
+    b=optional<nocopy>(nocopy(6));
+    EXPECT_EQ(nocopy(6),b.get());
+}
+
+namespace {
+    optional<double> odd_half(int n) {
+        optional<double> h;
+        if (n%2==1) h=n/2.0;
+        return h;
+    }
+}
+
+TEST(optionalm,bind) {
+    optional<int> a;
+    auto b=a.bind(odd_half);
+
+    EXPECT_EQ(typeid(optional<double>),typeid(b));
+
+    a=10;
+    b=a.bind(odd_half);
+    EXPECT_FALSE((bool)b);
+
+    a=11;
+    b=a.bind(odd_half);
+    EXPECT_TRUE((bool)b);
+    EXPECT_EQ(5.5,b.get());
+
+    b=a >> odd_half >> [](double x) { return (int)x; } >> odd_half;
+    EXPECT_TRUE((bool)b);
+    EXPECT_EQ(2.5,b.get());
+}
+
+TEST(optionalm,void) {
+    optional<void> a,b(true),c(a),d=b,e(false);
+
+    EXPECT_FALSE((bool)a);
+    EXPECT_TRUE((bool)b);
+    EXPECT_FALSE((bool)c);
+    EXPECT_TRUE((bool)d);
+    EXPECT_TRUE((bool)e);
+
+    auto x=a >> []() { return 1; };
+    EXPECT_FALSE((bool)x);
+
+    x=b >> []() { return 1; };
+    EXPECT_TRUE((bool)x);
+    EXPECT_EQ(1,x.get());
+}
+
+TEST(optionalm,bind_to_void) {
+    optional<int> a,b(3);
+    
+    int call_count=0;
+    auto vf=[&call_count](int i) -> void { ++call_count; };
+
+    auto x=a >> vf;
+    EXPECT_EQ(typeid(optional<void>),typeid(x));
+    EXPECT_FALSE((bool)x);
+    EXPECT_EQ(0,call_count);
+
+    call_count=0;
+    x=b >> vf;
+    EXPECT_TRUE((bool)x);
+    EXPECT_EQ(1,call_count);
+}
+    
+TEST(optionalm,bind_to_optional_void) {
+    optional<int> a,b(3),c(4);
+    
+    int count=0;
+    auto count_if_odd=[&count](int i) { return i%2?(++count,optional<void>(true)):optional<void>(); };
+
+    auto x=a >> count_if_odd;
+    EXPECT_EQ(typeid(optional<void>),typeid(x));
+    EXPECT_FALSE((bool)x);
+    EXPECT_EQ(0,count);
+
+    count=0;
+    x=b >> count_if_odd;
+    EXPECT_TRUE((bool)x);
+    EXPECT_EQ(1,count);
+
+    count=0;
+    x=c >> count_if_odd;
+    EXPECT_FALSE((bool)x);
+    EXPECT_EQ(0,count);
+}
+
+TEST(optionalm,bind_with_ref) {
+    optional<int> a=10;
+    a >> [](int &v) {++v; };
+    EXPECT_EQ(11,*a);
+}
+
+namespace {
+    struct check_cref {
+        int operator()(const int &) { return 10; }
+        int operator()(int &) { return 11; }
+    };
+}
+
+TEST(optionalm,bind_constness) {
+    check_cref checker;
+    optional<int> a=1;
+    int v=*(a >> checker);
+    EXPECT_EQ(11,v);
+
+    const optional<int> b=1;
+    v=*(b >> checker);
+    EXPECT_EQ(10,v);
+}
+
+
+TEST(optionalm,conversion) {
+    optional<double> a(3),b=5;
+    EXPECT_TRUE((bool)a);
+    EXPECT_TRUE((bool)b);
+    EXPECT_EQ(3.0,a.get());
+    EXPECT_EQ(5.0,b.get());
+
+    optional<int> x;
+    optional<double> c(x);
+    optional<double> d=optional<int>();
+    EXPECT_FALSE((bool)c);
+    EXPECT_FALSE((bool)d);
+
+    auto doubler=[](double x) { return x*2; };
+    auto y=optional<int>(3) >> doubler;
+    EXPECT_TRUE((bool)y);
+    EXPECT_EQ(6.0,y.get());
+}
+
+TEST(optionalm,or_operator) {
+    optional<const char *> default_msg="default";
+    auto x=nullptr | default_msg;
+    EXPECT_TRUE((bool)x);
+    EXPECT_STREQ("default",x.get());
+
+    auto y="something" | default_msg;
+    EXPECT_TRUE((bool)y);
+    EXPECT_STREQ("something",y.get());
+
+    optional<int> a(1),b,c(3);
+    EXPECT_EQ(1,*(a|b|c));
+    EXPECT_EQ(1,*(a|c|b));
+    EXPECT_EQ(1,*(b|a|c));
+    EXPECT_EQ(3,*(b|c|a));
+    EXPECT_EQ(3,*(c|a|b));
+    EXPECT_EQ(3,*(c|b|a));
+}
+
+TEST(optionalm,and_operator) {
+    optional<int> a(1);
+    optional<double> b(2.0);
+
+    auto ab=a&b;
+    auto ba=b&a;
+
+    EXPECT_EQ(typeid(ab),typeid(b));
+    EXPECT_EQ(typeid(ba),typeid(a));
+    EXPECT_EQ(2.0,*ab);
+    EXPECT_EQ(1,*ba);
+
+    auto zb=false & b;
+    EXPECT_EQ(typeid(zb),typeid(b));
+    EXPECT_FALSE((bool)zb);
+    
+    auto b3=b & 3;
+    EXPECT_EQ(typeid(b3),typeid(optional<int>));
+    EXPECT_TRUE((bool)b3);
+    EXPECT_EQ(3,*b3);
+}
+
+TEST(optionalm,provided) {
+    std::array<int,3> qs={1,0,3};
+    std::array<int,3> ps={14,14,14};
+    std::array<int,3> rs;
+
+    std::transform(ps.begin(),ps.end(),qs.begin(),rs.begin(),
+        [](int p,int q) { return *( provided(q!=0) >> [=]() { return p/q; } | -1 ); });
+
+    EXPECT_EQ(14,rs[0]);
+    EXPECT_EQ(-1,rs[1]);
+    EXPECT_EQ(4,rs[2]);
+}
diff --git a/tests/test_uninitialized.cpp b/tests/test_uninitialized.cpp
new file mode 100644
index 00000000..dcc9e47c
--- /dev/null
+++ b/tests/test_uninitialized.cpp
@@ -0,0 +1,200 @@
+#include "gtest.h"
+
+#include "util/uninitialized.hpp"
+
+using namespace nest::mc::util;
+
+namespace {
+    struct count_ops {
+        count_ops() {}
+        count_ops(const count_ops &n) { ++copy_ctor_count; }
+        count_ops(count_ops &&n) { ++move_ctor_count; }
+
+        count_ops &operator=(const count_ops &n) { ++copy_assign_count; return *this; }
+        count_ops &operator=(count_ops &&n) { ++move_assign_count; return *this; }
+
+        static int copy_ctor_count,copy_assign_count;
+        static int move_ctor_count,move_assign_count;
+        static void reset_counts() {
+            copy_ctor_count=copy_assign_count=0; 
+            move_ctor_count=move_assign_count=0;
+        }
+    };
+
+    int count_ops::copy_ctor_count=0;
+    int count_ops::copy_assign_count=0;
+    int count_ops::move_ctor_count=0;
+    int count_ops::move_assign_count=0;
+}
+
+TEST(uninitialized,ctor) {
+    count_ops::reset_counts();
+
+    uninitialized<count_ops> ua;
+    ua.construct(count_ops{});
+
+    count_ops b;
+    ua.construct(b);
+
+    EXPECT_EQ(1,count_ops::copy_ctor_count);
+    EXPECT_EQ(0,count_ops::copy_assign_count);
+    EXPECT_EQ(1,count_ops::move_ctor_count);
+    EXPECT_EQ(0,count_ops::move_assign_count);
+
+    ua.ref()=count_ops{};
+    ua.ref()=b;
+
+    EXPECT_EQ(1,count_ops::copy_ctor_count);
+    EXPECT_EQ(1,count_ops::copy_assign_count);
+    EXPECT_EQ(1,count_ops::move_ctor_count);
+    EXPECT_EQ(1,count_ops::move_assign_count);
+}
+
+namespace {
+    struct nocopy {
+        nocopy() {}
+        nocopy(const nocopy &n) = delete;
+        nocopy(nocopy &&n) { ++move_ctor_count; }
+
+        nocopy &operator=(const nocopy &n) = delete;
+        nocopy &operator=(nocopy &&n) { ++move_assign_count; return *this; }
+
+        static int move_ctor_count,move_assign_count;
+        static void reset_counts() { move_ctor_count=move_assign_count=0; }
+    };
+
+    int nocopy::move_ctor_count=0;
+    int nocopy::move_assign_count=0;
+}
+
+TEST(uninitialized,ctor_nocopy) {
+    nocopy::reset_counts();
+
+    uninitialized<nocopy> ua;
+    ua.construct(nocopy{});
+
+    EXPECT_EQ(1,nocopy::move_ctor_count);
+    EXPECT_EQ(0,nocopy::move_assign_count);
+
+    ua.ref()=nocopy{};
+
+    EXPECT_EQ(1,nocopy::move_ctor_count);
+    EXPECT_EQ(1,nocopy::move_assign_count);
+}
+
+namespace {
+    struct nomove {
+        nomove() {}
+        nomove(const nomove &n) { ++copy_ctor_count; }
+        nomove(nomove &&n) = delete;
+
+        nomove &operator=(const nomove &n) { ++copy_assign_count; return *this; }
+        nomove &operator=(nomove &&n) = delete;
+
+        static int copy_ctor_count,copy_assign_count;
+        static void reset_counts() { copy_ctor_count=copy_assign_count=0; }
+    };
+
+    int nomove::copy_ctor_count=0;
+    int nomove::copy_assign_count=0;
+}
+
+TEST(uninitialized,ctor_nomove) {
+    nomove::reset_counts();
+
+    uninitialized<nomove> ua;
+    ua.construct(nomove{}); // check against rvalue
+
+    nomove b;
+    ua.construct(b); // check against non-const lvalue
+
+    const nomove c;
+    ua.construct(c); // check against const lvalue
+
+    EXPECT_EQ(3,nomove::copy_ctor_count);
+    EXPECT_EQ(0,nomove::copy_assign_count);
+
+    nomove a;
+    ua.ref()=a;
+
+    EXPECT_EQ(3,nomove::copy_ctor_count);
+    EXPECT_EQ(1,nomove::copy_assign_count);
+}
+
+TEST(uninitialized,void) {
+    uninitialized<void> a,b;
+    a=b;
+
+    EXPECT_EQ(typeid(a.ref()),typeid(void));
+}
+
+TEST(uninitialized,ref) {
+    uninitialized<int &> x,y;
+    int a;
+
+    x.construct(a);
+    y=x;
+
+    x.ref()=2;
+    EXPECT_EQ(2,a);
+
+    y.ref()=3;
+    EXPECT_EQ(3,a);
+    EXPECT_EQ(3,x.cref());
+
+    EXPECT_EQ(&a,x.ptr());
+    EXPECT_EQ((const int *)&a,x.cptr());
+}
+
+namespace {
+    struct apply_tester {
+        mutable int op_count=0;
+        mutable int const_op_count=0;
+
+        int operator()(const int &a) const { ++const_op_count; return a+1; }
+        int operator()(int &a) const { ++op_count; return ++a; }
+    };
+}
+
+TEST(uninitialized,apply) {
+    uninitialized<int> ua;
+    ua.construct(10);
+
+    apply_tester A;
+    int r=ua.apply(A);
+    EXPECT_EQ(11,ua.cref());
+    EXPECT_EQ(11,r);
+
+    uninitialized<int &> ub;
+    ub.construct(ua.ref());
+
+    r=ub.apply(A);
+    EXPECT_EQ(12,ua.cref());
+    EXPECT_EQ(12,r);
+
+    uninitialized<const int &> uc;
+    uc.construct(ua.ref());
+
+    r=uc.apply(A);
+    EXPECT_EQ(12,ua.cref());
+    EXPECT_EQ(13,r);
+
+    const uninitialized<int> ud(ua);
+
+    r=ud.apply(A);
+    EXPECT_EQ(12,ua.cref());
+    EXPECT_EQ(12,ud.cref());
+    EXPECT_EQ(13,r);
+
+    EXPECT_EQ(2,A.op_count);
+    EXPECT_EQ(2,A.const_op_count);
+}
+
+TEST(uninitialized,void_apply) {
+    uninitialized<void> uv;
+
+    auto f=[]() { return 11; };
+    EXPECT_EQ(11,uv.apply(f));
+
+    EXPECT_EQ(12.5,uv.apply([]() { return 12.5; }));
+}
-- 
GitLab