#ifndef LTTNG_CONTAINER_WRAPPER_H
#define LTTNG_CONTAINER_WRAPPER_H
+#include <common/exception.hpp>
+#include <common/format.hpp>
#include <common/macros.hpp>
#include <cstddef>
*/
template <typename ContainerType, typename ElementType, typename ContainerOperations>
class random_access_container_wrapper {
+ template <typename IteratorContainerType, typename IteratorElementType>
class _iterator : public std::iterator<std::random_access_iterator_tag, std::size_t> {
public:
- explicit _iterator(const random_access_container_wrapper& container,
- std::size_t start_index = 0) :
+ explicit _iterator(IteratorContainerType& container, std::size_t start_index = 0) :
_container(container), _index(start_index)
{
}
return !(*this == other);
}
- typename std::conditional<std::is_pointer<ElementType>::value,
- ElementType,
- ElementType&>::type
- operator*() const noexcept
+ typename std::conditional<std::is_pointer<IteratorElementType>::value,
+ IteratorElementType,
+ IteratorElementType&>::type
+ operator*() const
{
return _container[_index];
}
private:
- const random_access_container_wrapper& _container;
+ IteratorContainerType& _container;
std::size_t _index;
};
- using iterator = _iterator;
+ using iterator = _iterator<random_access_container_wrapper, ElementType>;
+ using const_iterator = _iterator<const random_access_container_wrapper, const ElementType>;
public:
- explicit random_access_container_wrapper(ContainerType container) : _container{ container }
+ explicit random_access_container_wrapper(ContainerType container) :
+ _container{ std::move(container) }
{
}
return iterator(*this);
}
- iterator end() noexcept
+ iterator end()
{
- return iterator(*this, ContainerOperations::size(_container));
+ return iterator(*this, size());
}
- std::size_t size() const noexcept
+ const_iterator begin() const noexcept
+ {
+ return const_iterator(*this);
+ }
+
+ const_iterator end() const
+ {
+ return const_iterator(*this, size());
+ }
+
+ std::size_t size() const
{
return ContainerOperations::size(_container);
}
typename std::conditional<std::is_pointer<ElementType>::value, ElementType, ElementType&>::type
operator[](std::size_t index)
{
- LTTNG_ASSERT(index < ContainerOperations::size(_container));
- return ContainerOperations::get(_container, index);
+ /*
+ * To share code between the const and mutable versions of this operator, 'this'
+ * is casted to a const reference. A const_cast then ensures that a mutable
+ * reference (or pointer) is returned.
+ *
+ * We typically avoid const_cast, but this is safe: if the user is calling the
+ * mutable version of this operator, it had a mutable object anyhow.
+ *
+ * For more information, see Item 3 of Effective C++.
+ */
+ const auto& const_this = static_cast<const random_access_container_wrapper&>(*this);
+
+ /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) */
+ return const_cast<typename std::conditional<std::is_pointer<ElementType>::value,
+ ElementType,
+ ElementType&>::type>(const_this[index]);
}
typename std::conditional<std::is_pointer<ElementType>::value,
const ElementType&>::type
operator[](std::size_t index) const
{
- LTTNG_ASSERT(index < ContainerOperations::size(_container));
+ if (index >= ContainerOperations::size(_container)) {
+ LTTNG_THROW_INVALID_ARGUMENT_ERROR(lttng::format(
+ "Out of bound access through random_access_container_wrapper: index={}, size={}",
+ index,
+ size()));
+ }
+
return ContainerOperations::get(_container, index);
}
-private:
+protected:
ContainerType _container;
};
} /* namespace utils */