Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- K. Iglberger: Calling Functions: A Tutorial, CppCon 2020
- Scott Meyers, Effective C++ 3rd Edition, Item 23
- Scott Meyers: How Non-Member Functions Improve Encapsulation
- Herb Sutter: GotW #70: Encapsulation
- Herb Sutter, Exceptional C++ Style, Item 37 to Item 40
- Online: GoTW#84: Monoliths "Unstrung"
- Herb Sutter: What's In a Class? - The Interface Principle
- */
- #pragma once
- #include <algorithm>
- #include <memory>
- #include <cassert>
- #include <stdexcept>
- namespace API {
- template <typename T>
- class Container {
- public:
- using pointer = T*;
- using const_pointer = const T*;
- using iterator = T*;
- using const_iterator = const T*;
- using value_type = T;
- using size_type = std::size_t;
- using reference = T&;
- using const_reference = const T&;
- Container() = default;
- ~Container()
- {
- clear(*this);
- ::operator delete(_data);
- }
- explicit Container(size_type capacity)
- : _data(static_cast<pointer>(::operator new(sizeof(value_type) * capacity)))
- , _count(0)
- , _capacity(capacity)
- {
- //STL behavior is to fill with defaulted-Ts.
- //I'm ignoring that expection in order to delegate to this construction without paying for double-constructions.
- }
- Container(size_type count, const T& val)
- : Container(count)
- {
- std::uninitialized_fill_n(begin(*this), count, val); // copy construct each element w/ placement new
- _count = count;
- }
- //range construction
- Container(const_iterator begin, const_iterator end)
- : Container(static_cast<size_type>(std::distance(begin, end)))
- {
- assert(begin <= end && "Container(iter, iter): begin & end iterators are reversed");
- std::uninitialized_copy(begin, end, begin(*this)); // copy construct each element from range into buffer w/ placement new
- _count = static_cast<size_type>(std::distance(begin, end));
- }
- //copy ctor, delegating to range constructor
- explicit Container(const Container& that)
- : Container(that.begin(), that.end())
- {
- }
- //list construction, delegating to range constructor
- Container(std::initializer_list<T> list)
- : Container(std::begin(list), std::end(list))
- {
- }
- //move constructor
- Container(Container&& that) noexcept
- {
- _count = std::exchange(that._count, 0);
- _capacity = std::exchange(that._capacity, 0);
- _data = std::exchange(that._data, nullptr);
- }
- //by value assignment idiom. deals with both copy- and move assignment
- //also known as: "unifying assignment operator"
- //https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Copy-and-swap
- Container& operator=(Container that) noexcept
- {
- swap(that);
- return *this;
- }
- void swap(Container& that) noexcept
- {
- using std::swap;
- swap(_data, that._data);
- swap(_count, that._count);
- swap(_capacity, that._capacity);
- }
- void pop_back() noexcept
- {
- assert(!empty(*this) && "pop_back() on empty container is undefined!");
- back(*this).~value_type();
- --_count;
- }
- void reserve(size_type newCapacity){
- if (newCapacity <= _capacity) {
- return; //never decrease the allocation
- }
- pointer newBuffer = static_cast<pointer>(::operator new(sizeof(value_type) * newCapacity));
- std::uninitialized_move(begin(*this), end(*this), newBuffer);
- destruct_all(); //run all destructors on the moved-from objects.
- ::operator delete(_data);
- _data = newBuffer;
- _capacity = newCapacity;
- }
- template <typename... Args>
- void emplace_back(Args&&... args){
- resizeIfNeeded();
- new (&_data[_count]) value_type(std::forward<Args>(args)...); // construct value in memory of aligned storage using inplace operator new
- ++_count;
- }
- reference operator[](size_type index) noexcept {
- assert(index < size() && "Container operator[] index is out of range!");
- return _data[index];
- }
- const_reference operator[](size_type index) const noexcept{
- assert(index < size() && "Container operator[] index is out of range!");
- return _data[index];
- }
- pointer data() noexcept {
- return _data;
- }
- const_pointer data() const noexcept {
- return _data;
- }
- size_type capacity() const noexcept {
- return _capacity;
- }
- size_type size() const noexcept {
- return _count;
- }
- private:
- pointer _data = nullptr;
- size_type _count = 0;
- size_type _capacity = 0;
- constexpr static size_type INITIAL_CAPACITY = 2;
- constexpr static double GROWTH_FACTOR = 2.0f;
- void resizeIfNeeded()
- {
- if (_capacity == 0) {
- reserve(INITIAL_CAPACITY);
- } else if (_count == _capacity) {
- reserve(static_cast<size_type>(GROWTH_FACTOR * _capacity));
- }
- }
- void destruct_all() noexcept {
- for (reference item : *this) {
- item.~value_type();
- }
- }
- };
- template <typename T>
- void push_back(Container<T>& c, const T& value) {
- c.emplace_back(std::forward<const T&>(value));
- }
- template <typename T>
- void clear(Container<T>& c) noexcept {
- //TODO: could be erase(begin(), end()).
- while (!empty(c)) {
- c.pop_back();
- }
- }
- //ADL overload. See Arthur O'Dwyer: https://youtu.be/7Qgd9B1KuMQ?t=2597
- template <typename T>
- void swap(Container<T>& a, Container<T>& b) noexcept{
- a.swap(b);
- }
- template <typename T>
- typename Container<T>::reference at(Container<T>& c, typename Container<T>::size_type index)
- {
- if (index >= c.size()) {
- throw std::out_of_range("Container at() index is out of range!");
- }
- return c.data()[index];
- }
- template <typename T>
- typename Container<T>::const_reference at(const Container<T>& c, typename Container<T>::size_type index)
- {
- if (index >= c.size()) {
- throw std::out_of_range("Container at() index is out of range!");
- }
- return c.data()[index];
- }
- template <typename T>
- typename Container<T>::reference front(Container<T>& c) noexcept {
- assert(!empty(c) && "front() on empty container is UB");
- return c.data()[0];
- }
- template <typename T>
- typename Container<T>::const_reference front(const Container<T>& c) noexcept {
- assert(!empty(c) && "front() on empty container is UB");
- return c.data()[0];
- }
- template <typename T>
- typename Container<T>::const_reference back(const Container<T>& c) noexcept {
- assert(!empty(c) && "back() on empty container is UB");
- return c.data()[c.size() - 1];
- }
- template <typename T>
- typename Container<T>::reference back(Container<T>& c) noexcept {
- assert(!empty(c) && "back() on empty container is UB");
- return c.data()[c.size() - 1];
- }
- template <typename T>
- bool empty(const Container<T>& c) noexcept{
- return c.size() == 0;
- }
- template <typename T>
- typename Container<T>::iterator begin(Container<T>& c) noexcept { return c.data(); }
- template <typename T>
- typename Container<T>::const_iterator begin(const Container<T>& c) noexcept { return c.data(); }
- template <typename T>
- typename Container<T>::const_iterator cbegin(const Container<T>& c) noexcept { return c.data(); }
- template <typename T>
- typename Container<T>::const_iterator end(const Container<T>& c) noexcept { return c.data() + c.size(); }
- template <typename T>
- typename Container<T>::const_iterator cend(const Container<T>& c) noexcept { return c.data() + c.size(); }
- template <typename T>
- typename Container<T>::iterator end(Container<T>& c) noexcept { return c.data() + c.size(); }
- template <typename T>
- bool operator==(const Container<T>& lhs, const Container<T>& rhs) noexcept
- {
- if (std::size(lhs) != std::size(rhs)) {
- return false;
- }
- return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
- }
- template <typename T>
- bool operator<(const Container<T>& lhs, const Container<T>& rhs) noexcept
- {
- return std::lexicographical_compare(lhs.begin(), lhs.end(),
- rhs.begin(), rhs.end());
- }
- template <typename T>
- bool operator!=(const Container<T>& lhs, const Container<T>& rhs) noexcept
- {
- return !(lhs == rhs);
- }
- template <typename T>
- bool operator>(const Container<T>& lhs, const Container<T>& rhs) noexcept
- {
- return rhs < lhs;
- }
- template <typename T>
- bool operator<=(const Container<T>& lhs, const Container<T>& rhs) noexcept
- {
- !(lhs > rhs);
- }
- template <typename T>
- bool operator>=(const Container<T>& lhs, const Container<T>& rhs) noexcept
- {
- !(lhs < rhs);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement