LazyConstructor

This commit is contained in:
Pavel Kirienko 2014-03-08 15:19:41 +04:00
parent bc065ee3c4
commit fd454a77f8
2 changed files with 206 additions and 0 deletions

View File

@ -0,0 +1,145 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <cstdlib>
#include <cassert>
#include <stdexcept>
namespace uavcan
{
template <typename T>
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<T>& rhs)
: ptr_(NULL)
{
if (rhs)
construct(*rhs); // Invoke copy constructor
}
~LazyConstructor() { destroy(); }
LazyConstructor<T>& operator=(const LazyConstructor<T>& 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<void*>(data_)) T();
}
// MAX_ARGS = 6
// TEMPLATE = '''
// template <%s>
// void construct(%s)
// {
// ensureNotConstructed();
// ptr_ = new (static_cast<void*>(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 <typename P1>
void construct(P1 p1)
{
ensureNotConstructed();
ptr_ = new (static_cast<void*>(data_)) T(p1);
}
template <typename P1, typename P2>
void construct(P1 p1, P2 p2)
{
ensureNotConstructed();
ptr_ = new (static_cast<void*>(data_)) T(p1, p2);
}
template <typename P1, typename P2, typename P3>
void construct(P1 p1, P2 p2, P3 p3)
{
ensureNotConstructed();
ptr_ = new (static_cast<void*>(data_)) T(p1, p2, p3);
}
template <typename P1, typename P2, typename P3, typename P4>
void construct(P1 p1, P2 p2, P3 p3, P4 p4)
{
ensureNotConstructed();
ptr_ = new (static_cast<void*>(data_)) T(p1, p2, p3, p4);
}
template <typename P1, typename P2, typename P3, typename P4, typename P5>
void construct(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5)
{
ensureNotConstructed();
ptr_ = new (static_cast<void*>(data_)) T(p1, p2, p3, p4, p5);
}
template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
void construct(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6)
{
ensureNotConstructed();
ptr_ = new (static_cast<void*>(data_)) T(p1, p2, p3, p4, p5, p6);
}
};
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#include <gtest/gtest.h>
#include <uavcan/internal/lazy_constructor.hpp>
TEST(LazyConstructor, Basic)
{
using ::uavcan::LazyConstructor;
LazyConstructor<std::string> a;
LazyConstructor<std::string> 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<std::string> 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);
}