Program Listing for File bounded_vector.hpp

Return to documentation for file (include/rosidl_runtime_cpp/bounded_vector.hpp)

// Copyright 2016 Open Source Robotics Foundation, 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 ROSIDL_RUNTIME_CPP__BOUNDED_VECTOR_HPP_
#define ROSIDL_RUNTIME_CPP__BOUNDED_VECTOR_HPP_

#include <algorithm>
#include <memory>
#include <stdexcept>
#include <utility>
#include <vector>

namespace rosidl_runtime_cpp
{


template<typename Tp, std::size_t UpperBound, typename Alloc = std::allocator<Tp>>
class BoundedVector
  : protected std::vector<Tp, Alloc>
{
  using Base = std::vector<Tp, Alloc>;

public:
  using typename Base::value_type;
  using typename Base::pointer;
  using typename Base::const_pointer;
  using typename Base::reference;
  using typename Base::const_reference;
  using typename Base::iterator;
  using typename Base::const_iterator;
  using typename Base::const_reverse_iterator;
  using typename Base::reverse_iterator;
  using typename Base::size_type;
  using typename Base::difference_type;
  using typename Base::allocator_type;

  BoundedVector()
  noexcept (std::is_nothrow_default_constructible<Alloc>::value)
  : Base()
  {}


  explicit
  BoundedVector(
    const typename Base::allocator_type & a)
  noexcept
  : Base(a)
  {}


  explicit
  BoundedVector(
    typename Base::size_type n,
    const typename Base::allocator_type & a = allocator_type())
  : Base(n, a)
  {
    if (n > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
  }


  BoundedVector(
    typename Base::size_type n,
    const typename Base::value_type & value,
    const typename Base::allocator_type & a = allocator_type())
  : Base(n, value, a)
  {
    if (n > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
  }


  BoundedVector(
    const BoundedVector & x)
  : Base(x)
  {}


  BoundedVector(BoundedVector && x) noexcept
  : Base(std::move(x))
  {}

  BoundedVector(const BoundedVector & x, const typename Base::allocator_type & a)
  : Base(x, a)
  {}


  BoundedVector(
    std::initializer_list<typename Base::value_type> l,
    const typename Base::allocator_type & a = typename Base::allocator_type())
  : Base(l, a)
  {
    if (l.size() > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
  }


  template<
    typename InputIterator
  >
  BoundedVector(
    InputIterator first,
    InputIterator last,
    const typename Base::allocator_type & a = allocator_type())
  : Base(first, last, a)
  {
    if (size() > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
  }


  ~BoundedVector() noexcept
  {}


  BoundedVector &
  operator=(const BoundedVector & x)
  {
    (void)Base::operator=(x);
    return *this;
  }


  BoundedVector &
  operator=(BoundedVector && x)
  {
    (void)Base::operator=(std::move(x));
    return *this;
  }


  BoundedVector &
  operator=(std::initializer_list<typename Base::value_type> l)
  {
    if (l.size() > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    Base::operator=(l);
    return *this;
  }


  void
  assign(
    typename Base::size_type n,
    const typename Base::value_type & val)
  {
    if (n > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    Base::assign(n, val);
  }


  template<
    typename InputIterator
  >
  void
  assign(InputIterator first, InputIterator last)
  {
    using cat = typename std::iterator_traits<InputIterator>::iterator_category;
    do_assign(first, last, cat());
  }


  void
  assign(std::initializer_list<typename Base::value_type> l)
  {
    if (l.size() > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    Base::assign(l);
  }

  using Base::begin;
  using Base::end;
  using Base::rbegin;
  using Base::rend;
  using Base::cbegin;
  using Base::cend;
  using Base::crbegin;
  using Base::crend;
  using Base::size;

  typename Base::size_type
  max_size() const noexcept
  {
    return std::min(UpperBound, Base::max_size());
  }


  void
  resize(typename Base::size_type new_size)
  {
    if (new_size > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    Base::resize(new_size);
  }


  void
  resize(
    typename Base::size_type new_size,
    const typename Base::value_type & x)
  {
    if (new_size > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    Base::resize(new_size, x);
  }

  using Base::shrink_to_fit;
  using Base::capacity;
  using Base::empty;


  void
  reserve(typename Base::size_type n)
  {
    if (n > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    Base::reserve(n);
  }

  using Base::operator[];
  using Base::at;
  using Base::front;
  using Base::back;


  template<
    typename T,
    typename std::enable_if<
      !std::is_same<T, Tp>::value &&
      !std::is_same<T, bool>::value
    >::type * = nullptr
  >
  T *
  data() noexcept
  {
    return Base::data();
  }

  template<
    typename T,
    typename std::enable_if<
      !std::is_same<T, Tp>::value &&
      !std::is_same<T, bool>::value
    >::type * = nullptr
  >
  const T *
  data() const noexcept
  {
    return Base::data();
  }


  void
  push_back(const typename Base::value_type & x)
  {
    if (size() >= UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    Base::push_back(x);
  }

  void
  push_back(typename Base::value_type && x)
  {
    if (size() >= UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    Base::push_back(x);
  }


  template<typename ... Args>
  auto
  emplace_back(Args && ... args)
  {
    if (size() >= UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    return Base::emplace_back(std::forward<Args>(args)...);
  }


  template<typename ... Args>
  typename Base::iterator
  emplace(
    typename Base::const_iterator position,
    Args && ... args)
  {
    if (size() >= UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    return Base::emplace(position, std::forward<Args>(args) ...);
  }


  typename Base::iterator
  insert(
    typename Base::const_iterator position,
    const typename Base::value_type & x)
  {
    if (size() >= UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    return Base::insert(position, x);
  }


  typename Base::iterator
  insert(
    typename Base::const_iterator position,
    typename Base::value_type && x)
  {
    if (size() >= UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    return Base::insert(position, x);
  }


  typename Base::iterator
  insert(
    typename Base::const_iterator position,
    std::initializer_list<typename Base::value_type> l)
  {
    if (size() + l.size() > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    return Base::insert(position, l);
  }


  typename Base::iterator
  insert(
    typename Base::const_iterator position,
    typename Base::size_type n,
    const typename Base::value_type & x)
  {
    if (size() + n > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    return Base::insert(position, n, x);
  }


  template<
    typename InputIterator
  >
  typename Base::iterator
  insert(
    typename Base::const_iterator position,
    InputIterator first,
    InputIterator last)
  {
    using cat = typename std::iterator_traits<InputIterator>::iterator_category;
    return do_insert(position, first, last, cat());
  }

  using Base::erase;
  using Base::pop_back;
  using Base::clear;

private:
  template<
    typename InputIterator
  >
  void
  do_assign(InputIterator first, InputIterator last, std::input_iterator_tag)
  {
    BoundedVector(first, last).swap(*this);
  }

  template<
    typename FwdIterator
  >
  void
  do_assign(FwdIterator first, FwdIterator last, std::forward_iterator_tag)
  {
    if (static_cast<std::size_t>(std::distance(first, last)) > UpperBound) {
      throw std::length_error("Exceeded upper bound");
    }
    Base::assign(first, last);
  }

  // Insert each value at the end and then rotate them to the desired position.
  // If the bound is exceeded, the inserted elements are removed again.
  template<
    typename InputIterator
  >
  typename Base::iterator
  do_insert(
    typename Base::const_iterator position,
    InputIterator first,
    InputIterator last,
    std::input_iterator_tag)
  {
    const auto orig_size = size();
    const auto idx = position - cbegin();
    try {
      while (first != last) {
        push_back(*first++);
      }
    } catch (const std::length_error &) {
      Base::resize(orig_size);
      throw;
    }
    auto pos = begin() + idx;
    std::rotate(pos, begin() + orig_size, end());
    return begin() + idx;
  }

  template<
    typename FwdIterator
  >
  typename Base::iterator
  do_insert(
    typename Base::const_iterator position,
    FwdIterator first,
    FwdIterator last,
    std::forward_iterator_tag)
  {
    auto dist = std::distance(first, last);
    if ((dist < 0) || (size() + static_cast<size_t>(dist) > UpperBound)) {
      throw std::length_error("Exceeded upper bound");
    }
    return Base::insert(position, first, last);
  }


  friend bool
  operator==(
    const BoundedVector & x,
    const BoundedVector & y)
  {
    return static_cast<const Base &>(x) == static_cast<const Base &>(y);
  }


  friend bool
  operator<(
    const BoundedVector & x,
    const BoundedVector & y)
  {
    return static_cast<const Base &>(x) < static_cast<const Base &>(y);
  }

  friend bool
  operator!=(
    const BoundedVector & x,
    const BoundedVector & y)
  {
    return static_cast<const Base &>(x) != static_cast<const Base &>(y);
  }

  friend bool
  operator>(
    const BoundedVector & x,
    const BoundedVector & y)
  {
    return static_cast<const Base &>(x) > static_cast<const Base &>(y);
  }

  friend bool
  operator<=(
    const BoundedVector & x,
    const BoundedVector & y)
  {
    return static_cast<const Base &>(x) <= static_cast<const Base &>(y);
  }

  friend bool
  operator>=(
    const BoundedVector & x,
    const BoundedVector & y)
  {
    return static_cast<const Base &>(x) >= static_cast<const Base &>(y);
  }
};

template<typename Tp, std::size_t UpperBound, typename Alloc>
inline void
swap(BoundedVector<Tp, UpperBound, Alloc> & x, BoundedVector<Tp, UpperBound, Alloc> & y)
{
  x.swap(y);
}

}  // namespace rosidl_runtime_cpp

#endif  // ROSIDL_RUNTIME_CPP__BOUNDED_VECTOR_HPP_