From fd454a77f8e18d848fa4dfa09fb3ce6ffb18f19b Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Sat, 8 Mar 2014 15:19:41 +0400 Subject: [PATCH] LazyConstructor --- .../uavcan/internal/lazy_constructor.hpp | 145 ++++++++++++++++++ libuavcan/test/lazy_constructor.cpp | 61 ++++++++ 2 files changed, 206 insertions(+) create mode 100644 libuavcan/include/uavcan/internal/lazy_constructor.hpp create mode 100644 libuavcan/test/lazy_constructor.cpp diff --git a/libuavcan/include/uavcan/internal/lazy_constructor.hpp b/libuavcan/include/uavcan/internal/lazy_constructor.hpp new file mode 100644 index 0000000000..d20b37a072 --- /dev/null +++ b/libuavcan/include/uavcan/internal/lazy_constructor.hpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2014 Pavel Kirienko + */ + +#pragma once + +#include +#include +#include + +namespace uavcan +{ + +template +class LazyConstructor +{ + unsigned char data_[sizeof(T)] __attribute__((aligned(16))); // TODO: compiler-independent alignment + T* ptr_; + + void failure() const + { +#if UAVCAN_EXCEPTIONS + throw std::logic_error(typeid(*this).name()); +#else + assert(0); + std::abort(); +#endif + } + + void ensureConstructed() const + { + if (!ptr_) + failure(); + } + + void ensureNotConstructed() const + { + if (ptr_) + failure(); + } + +public: + LazyConstructor() + : ptr_(NULL) + { } + + LazyConstructor(const LazyConstructor& rhs) + : ptr_(NULL) + { + if (rhs) + construct(*rhs); // Invoke copy constructor + } + + ~LazyConstructor() { destroy(); } + + LazyConstructor& operator=(const LazyConstructor& rhs) + { + destroy(); + if (rhs) + construct(*rhs); // Invoke copy constructor + return *this; + } + + bool isConstructed() const { return ptr_ != NULL; } + + operator T*() const { return ptr_; } + + const T* operator->() const { ensureConstructed(); return ptr_; } + T* operator->() { ensureConstructed(); return ptr_; } + + const T& operator*() const { ensureConstructed(); return *ptr_; } + T& operator*() { ensureConstructed(); return *ptr_; } + + void destroy() + { + if (ptr_) + ptr_->~T(); + ptr_ = NULL; + } + + void construct() + { + ensureNotConstructed(); + ptr_ = new (static_cast(data_)) T(); + } + +// MAX_ARGS = 6 +// TEMPLATE = ''' +// template <%s> +// void construct(%s) +// { +// ensureNotConstructed(); +// ptr_ = new (static_cast(data_)) T(%s); +// }''' +// for nargs in range(1, MAX_ARGS + 1): +// nums = [(x + 1) for x in range(nargs)] +// l1 = ['typename P%d' % x for x in nums] +// l2 = ['P%d p%d' % (x, x) for x in nums] +// l3 = ['p%d' % x for x in nums] +// print(TEMPLATE % (', '.join(l1), ', '.join(l2), ', '.join(l3))) + + template + void construct(P1 p1) + { + ensureNotConstructed(); + ptr_ = new (static_cast(data_)) T(p1); + } + + template + void construct(P1 p1, P2 p2) + { + ensureNotConstructed(); + ptr_ = new (static_cast(data_)) T(p1, p2); + } + + template + void construct(P1 p1, P2 p2, P3 p3) + { + ensureNotConstructed(); + ptr_ = new (static_cast(data_)) T(p1, p2, p3); + } + + template + void construct(P1 p1, P2 p2, P3 p3, P4 p4) + { + ensureNotConstructed(); + ptr_ = new (static_cast(data_)) T(p1, p2, p3, p4); + } + + template + void construct(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) + { + ensureNotConstructed(); + ptr_ = new (static_cast(data_)) T(p1, p2, p3, p4, p5); + } + + template + void construct(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6) + { + ensureNotConstructed(); + ptr_ = new (static_cast(data_)) T(p1, p2, p3, p4, p5, p6); + } +}; + +} diff --git a/libuavcan/test/lazy_constructor.cpp b/libuavcan/test/lazy_constructor.cpp new file mode 100644 index 0000000000..42d9ff0b43 --- /dev/null +++ b/libuavcan/test/lazy_constructor.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 Pavel Kirienko + */ + +#include +#include + + +TEST(LazyConstructor, Basic) +{ + using ::uavcan::LazyConstructor; + + LazyConstructor a; + LazyConstructor b; + + ASSERT_FALSE(a); + ASSERT_FALSE(b.isConstructed()); + + /* + * Construction + */ + a.destroy(); // no-op + a.construct(); + b.construct("Hello world"); + + ASSERT_TRUE(a); + ASSERT_TRUE(b.isConstructed()); + + ASSERT_NE(*a, *b); + ASSERT_STRNE(a->c_str(), b->c_str()); + + ASSERT_EQ(*a, ""); + ASSERT_EQ(*b, "Hello world"); + + /* + * Copying + */ + a = b; // Assignment operator performs destruction and immediate copy construction + ASSERT_EQ(*a, *b); + ASSERT_EQ(*a, "Hello world"); + + LazyConstructor c(a); // Copy constructor call is forwarded to std::string + + ASSERT_EQ(*c, *a); + + *a = "123"; + ASSERT_NE(*c, *a); + ASSERT_EQ(*c, *b); + + *c = "456"; + ASSERT_NE(*a, *c); + ASSERT_NE(*b, *a); + ASSERT_NE(*c, *b); + + /* + * Destruction + */ + ASSERT_TRUE(c); + c.destroy(); + ASSERT_FALSE(c); +}