From ca63485d125d85fc1e9239001894a023d1214406 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Wed, 14 Jun 2017 17:45:43 +1000 Subject: [PATCH] sarray: add a simple static storage/dynamic size array --- CMakeLists.txt | 3 ++ sarray.cpp | 17 +++++++++ sarray.hpp | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ test/sarray.cpp | 68 +++++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 sarray.cpp create mode 100644 sarray.hpp create mode 100644 test/sarray.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 88e070e5..1a275ad1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -366,6 +366,8 @@ list ( region.hpp region.ipp roots/bisection.hpp + sarray.cpp + sarray.hpp si.cpp signal.cpp signal.hpp @@ -492,6 +494,7 @@ if (TESTS) rational region roots/bisection + sarray signal stream string diff --git a/sarray.cpp b/sarray.cpp new file mode 100644 index 00000000..287fb71c --- /dev/null +++ b/sarray.cpp @@ -0,0 +1,17 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2017 Danny Robson + */ + +#include "./sarray.hpp" diff --git a/sarray.hpp b/sarray.hpp new file mode 100644 index 00000000..18f342ac --- /dev/null +++ b/sarray.hpp @@ -0,0 +1,94 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2017 Danny Robson + */ + +#ifndef CRUFT_UTIL_SARRAY_HPP +#define CRUFT_UTIL_SARRAY_HPP + +#include "./iterator.hpp" + +#include + +namespace util { + /// An array with constant maximum size, but with actual used storage + /// capacity fixed at construction time. + /// + /// \tparam S maximum number of elements + /// \tparam T data type of elements + template + class sarray { + public: + //--------------------------------------------------------------------- + template + explicit + sarray (const ContainerT &_data): + m_size (std::size (_data)) + { + if (m_size > S) + throw std::length_error("exceeded maximum size"); + + // I'd like to use izip and structured bindings here, but we run + // afoul of clang#3349 so we use a seperate index variable. + std::size_t i = 0; + for (auto &d: _data) { + ::new (&m_data.objects[i++]) (T) (d); + } + } + + ~sarray () + { + for (std::size_t i = 0; i < m_size; ++i) + m_data.objects[i].~T (); + } + + //--------------------------------------------------------------------- + std::size_t size (void) const + { + return m_size; + } + + //--------------------------------------------------------------------- + T& operator[] (std::size_t i)& { return m_data.objects[i]; } + const T& operator[] (std::size_t i) const& { return m_data.objects[i]; } + + auto begin (void) { return std::begin (m_data.objects); }; + auto end (void) { return begin () + m_size; } + + auto begin (void) const { return std::begin (m_data.objects); }; + auto end (void) const { return begin () + m_size; } + + auto cbegin (void) const { return std::cbegin (m_data.objects); }; + auto cend (void) const { return cbegin () + m_size; } + + private: + union alignas (T) data_t { + // we manually define a trivial constructor/destructor to appease + // gcc which doesn't like us using non-trivial T as the stored + // object. + // + // we're explicitly controlling the lifetime ourselves so this + // isn't a functional issue. + data_t () { }; + ~data_t () { }; + + char store[sizeof (T) * S]; + T objects[S]; + } m_data; + + const std::size_t m_size; + }; +} + +#endif diff --git a/test/sarray.cpp b/test/sarray.cpp new file mode 100644 index 00000000..e44d5c28 --- /dev/null +++ b/test/sarray.cpp @@ -0,0 +1,68 @@ +#include "tap.hpp" + +#include "sarray.hpp" +#include "debug.hpp" + +#include + + +/////////////////////////////////////////////////////////////////////////////// +// A simple wrapper over an integer that increments at construction time, and +// decrements at destruction time. Used to check for obvious leakages in +// sarray lifetimes. +struct counter { + explicit counter (int &_var): + var (_var) + { ++var; } + + counter (const counter &rhs): + counter (rhs.var) + { ; } + + ~counter () + { --var; } + + int &var; +}; + + +/////////////////////////////////////////////////////////////////////////////// +int +main (int, char**) +{ + util::TAP::logger tap; + + int count = 0; + + std::vector objects; + std::generate_n (std::back_inserter (objects), 3, [&count] () { return counter (count); }); + CHECK_GT (objects.size (), 0u); + + { + util::sarray<8,counter> array (objects); + tap.expect_eq ( + count, + static_cast (objects.size () + array.size ()), + "array constructors executed" + ); + + tap.expect_eq ( + objects.size (), + array.size (), + "array construction preserves size" + ); + } + + tap.expect_eq ( + static_cast (objects.size ()), + count, + "array destructors executed" + ); + + tap.expect_throw ([] () { + float data[] = { 0.f, 1.f, 2.f }; + util::sarray<2, float> value { data }; + }, "oversized array initializer throws std::length_error"); + + return tap.status (); +} \ No newline at end of file