Program Listing for File static_vector.hpp

Return to documentation for file (/tmp/ws/src/apex_containers/apex_containers/include/apex_containers/static_vector.hpp)

// Copyright 2018 Apex.AI, Inc.
//
// 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.


#ifndef APEX_CONTAINERS__STATIC_VECTOR_HPP_
#define APEX_CONTAINERS__STATIC_VECTOR_HPP_

#include <algorithm>
#include <vector>
#include <typeinfo>
#include <utility>
#include <memory>
namespace apex
{
namespace _vector_impl
{

template<class U>
class single_allocator
{
public:
  using base_type = std::allocator<U>;
  using value_type = U;

  using propagate_on_container_copy_assignment = std::true_type;  // for consistency
  using propagate_on_container_move_assignment = std::true_type;  // to avoid the pessimization
  using propagate_on_container_swap = std::true_type;  // to avoid the undefined behavior

  explicit single_allocator(const typename base_type::size_type n)
  : m_size(n), m_consumed(false)
  {
    // Checking that we are not trying to allocate more than the base allocator supports.
    if (n > m_alloc.max_size()) {
      throw std::bad_alloc();
    }
  }

  single_allocator(const single_allocator<U> & other) // NOLINT #2459
  : m_size(other.m_size), m_consumed(other.m_consumed), m_alloc(other.m_alloc) {}

  single_allocator<U> & operator=(const _vector_impl::single_allocator<U> & other)
  {
    m_size = other.m_size;
    m_consumed = other.m_consumed;
    m_alloc = other.m_alloc;
    return *this;
  }

  single_allocator(single_allocator<U> && other) noexcept
  : m_size(std::move(other.m_size)),
    m_consumed(std::move(other.m_consumed)),
    m_alloc(std::move(other.m_alloc)) {}

  single_allocator<U> & operator=(single_allocator<U> && other) noexcept
  {
    m_size = std::move(other.m_size);
    m_consumed = std::move(other.m_consumed);
    m_alloc = std::move(other.m_alloc);
    return *this;
  }

  typename base_type::pointer allocate(
    const typename base_type::size_type n, const void * hint = 0)
  {
    if (n != m_size || m_consumed) {
      throw std::bad_alloc();
    }
    // Ensuring exception safety by getting the memory before marking the allocator as used.
    auto && ret = m_alloc.allocate(n, hint);
    m_consumed = true;
    return ret;
  }

  void deallocate(typename base_type::pointer ptr, typename base_type::size_type n)
  {
    m_alloc.deallocate(ptr, n);
  }

  typename base_type::size_type max_size() const
  {
    return m_size;
  }

private:
  typename base_type::size_type m_size;
  bool m_consumed;
  base_type m_alloc;
};  // _vector_impl::single_allocator

template<typename T, typename U>
inline bool operator==(const single_allocator<T> &, const single_allocator<U> &)
{
  // Memory fom a _vector_impl::single_allocator can always be deallocated by a different
  // single_allocator.
  return true;
}

template<typename T, typename U>
inline bool operator!=(const single_allocator<T> & a, const single_allocator<U> & b)
{
  return !(a == b);
}

}  // namespace _vector_impl
#define APEX_STATIC_VECTOR_FWD(member, name) \
  decltype(auto) name() {return member.name();} \
  decltype(auto) name() const {return member.name();}

#define APEX_STATIC_VECTOR_FWD_PARAM(member, name) \
  template<class ... Params> \
  decltype(auto) name(Params && ... params) { \
    return member.name(std::forward<Params>(params) ...);} \
  template<class ... Params> \
  decltype(auto) name(Params && ... params) const { \
    return member.name(std::forward<Params>(params) ...);}

#define APEX_STATIC_VECTOR_DEFINITIONS_GEN(type, name) \
  using name = typename type::name;

template<class T>
class static_vector
{
private:
  using vector_type = std::vector<T, _vector_impl::single_allocator<T>>;
  std::vector<T, _vector_impl::single_allocator<T>> m_v;

public:
  APEX_STATIC_VECTOR_DEFINITIONS_GEN(vector_type, value_type)
  APEX_STATIC_VECTOR_DEFINITIONS_GEN(vector_type, allocator_type)
  APEX_STATIC_VECTOR_DEFINITIONS_GEN(vector_type, reference)
  APEX_STATIC_VECTOR_DEFINITIONS_GEN(vector_type, const_reference)
  APEX_STATIC_VECTOR_DEFINITIONS_GEN(vector_type, pointer)
  APEX_STATIC_VECTOR_DEFINITIONS_GEN(vector_type, const_pointer)
  APEX_STATIC_VECTOR_DEFINITIONS_GEN(vector_type, iterator)
  APEX_STATIC_VECTOR_DEFINITIONS_GEN(vector_type, const_iterator)
  APEX_STATIC_VECTOR_DEFINITIONS_GEN(vector_type, reverse_iterator)
  APEX_STATIC_VECTOR_DEFINITIONS_GEN(vector_type, const_reverse_iterator)
  APEX_STATIC_VECTOR_DEFINITIONS_GEN(vector_type, difference_type)
  APEX_STATIC_VECTOR_DEFINITIONS_GEN(vector_type, size_type)
  explicit static_vector(const size_type capacity)
  : m_v(_vector_impl::single_allocator<T>(capacity))
  {
    m_v.reserve(capacity);
  }

  template<class IT>
  explicit static_vector(
    const size_type capacity,
    const IT first,
    const IT last)
  : m_v(_vector_impl::single_allocator<T>(capacity))
  {
    const auto dist = std::distance(first, last);
    if (dist > static_cast<decltype(dist)>(capacity)) {
      throw std::length_error("[first, last) range error");
    }
    m_v.reserve(capacity);
    // Distance is positive per above condition.
    m_v.resize(std::distance(first, last));
    std::copy(first, last, m_v.begin());
  }
  explicit static_vector(
    const size_type capacity,
    std::initializer_list<value_type> il)
  : m_v(_vector_impl::single_allocator<T>(capacity))
  {
    if (il.size() > capacity) {
      throw std::length_error("initializer list too long");
    }
    m_v.reserve(capacity);
    m_v.resize(il.size());
    std::copy(il.begin(), il.end(), m_v.begin());
  }

  static_vector(const static_vector<T> & other) = delete;
  static_vector<T> & operator=(const static_vector<T> & other)
  {
    m_v = other.m_v;
    return *this;
  }

  static_vector(static_vector<T> && other) noexcept
  : m_v(std::move(other.m_v)) {}

  static_vector<T> & operator=(static_vector<T> && other) noexcept
  {
    m_v = std::move(other.m_v);
    return *this;
  }

  void shrink_to_fit()
  {
    // The C++ standard allows shrink to fit to be a noop which is the case here to prevent any
    // reallocation.
  }

  void swap(static_vector<T> & x)
  {
    m_v.swap(x.m_v);
  }

  APEX_STATIC_VECTOR_FWD(m_v, begin)
  APEX_STATIC_VECTOR_FWD(m_v, end)
  APEX_STATIC_VECTOR_FWD(m_v, rbegin)
  APEX_STATIC_VECTOR_FWD(m_v, rend)
  APEX_STATIC_VECTOR_FWD(m_v, cbegin)
  APEX_STATIC_VECTOR_FWD(m_v, cend)
  APEX_STATIC_VECTOR_FWD(m_v, crbegin)
  APEX_STATIC_VECTOR_FWD(m_v, crend)

  APEX_STATIC_VECTOR_FWD(m_v, size)
  APEX_STATIC_VECTOR_FWD(m_v, max_size)
  APEX_STATIC_VECTOR_FWD_PARAM(m_v, resize)
  APEX_STATIC_VECTOR_FWD(m_v, capacity)
  APEX_STATIC_VECTOR_FWD(m_v, empty)
  APEX_STATIC_VECTOR_FWD_PARAM(m_v, reserve)

  APEX_STATIC_VECTOR_FWD_PARAM(m_v, operator[])
  APEX_STATIC_VECTOR_FWD_PARAM(m_v, at)
  APEX_STATIC_VECTOR_FWD(m_v, front)
  APEX_STATIC_VECTOR_FWD(m_v, back)
  APEX_STATIC_VECTOR_FWD(m_v, data)

  APEX_STATIC_VECTOR_FWD_PARAM(m_v, assign)
  APEX_STATIC_VECTOR_FWD_PARAM(m_v, push_back)
  APEX_STATIC_VECTOR_FWD(m_v, pop_back)
  APEX_STATIC_VECTOR_FWD_PARAM(m_v, insert)
  APEX_STATIC_VECTOR_FWD_PARAM(m_v, erase)
  APEX_STATIC_VECTOR_FWD(m_v, clear)
  APEX_STATIC_VECTOR_FWD_PARAM(m_v, emplace)
  APEX_STATIC_VECTOR_FWD_PARAM(m_v, emplace_back)

  template<class K>
  friend bool operator==(const static_vector<K> & lhs, const static_vector<K> & rhs);
  template<class K>
  friend bool operator!=(const static_vector<K> & lhs, const static_vector<K> & rhs);
  template<class K>
  friend bool operator<(const static_vector<K> & lhs, const static_vector<K> & rhs);
  template<class K>
  friend bool operator<=(const static_vector<K> & lhs, const static_vector<K> & rhs);
  template<class K>
  friend bool operator>(const static_vector<K> & lhs, const static_vector<K> & rhs);
  template<class K>
  friend bool operator>=(const static_vector<K> & lhs, const static_vector<K> & rhs);
};

#define APEX_VECTOR_CMP_OP(op, member) \
  template<class K> \
  bool operator op( \
    const static_vector<K> & lhs, \
    const static_vector<K> & rhs) { \
    return operator op(lhs.member, rhs.member); \
  }

APEX_VECTOR_CMP_OP(==, m_v)
APEX_VECTOR_CMP_OP(!=, m_v)
APEX_VECTOR_CMP_OP(<, m_v) // NOLINT
APEX_VECTOR_CMP_OP(<=, m_v)
APEX_VECTOR_CMP_OP(>, m_v) // NOLINT
APEX_VECTOR_CMP_OP(>=, m_v)
}  // namespace apex

namespace std
{
template<class T>
void swap(apex::static_vector<T> & rhs, apex::static_vector<T> & lhs)
{
  rhs.swap(lhs);
}
}  // namespace std
#endif  // APEX_CONTAINERS__STATIC_VECTOR_HPP_