ulfben icon

Container 2020 day 2 (refactoring II)

ulfben | PRO | 11/22/20 07:57:41 PM UTC (Edited) | 0 ⭐ | 1887 👁️ | Never ⏰ | []
C++ |

8.1 KB

|

None

|

0 👍

/

0 👎

/*
Did some reading and tried to separate the lifetime of the data buffer from the lifetimes of the Ts *in* the buffer.
Ergo: create an uninitialized block of memory and manually construct / destruct the Ts inside of it as needed.
 
Helpful resources:
https://codereview.stackexchange.com/questions/211241/simple-re-implementation-of-stdvector
https://lokiastari.com/blog/2016/02/27/vector/index.html
https://lokiastari.com/blog/2016/02/29/vector-resource-management-ii-copy-assignment/index.html
*/
#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();
        ::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 tradition in ordet to delegate to this construction without paying for double-constructions.
    }
        
    Container(size_type count, const T& val)
        : Container(count)
    {
        std::uninitialized_fill_n(begin(), 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()); // 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);
    }
 
    //ADL overload. See Arthur O'Dwyer: https://youtu.be/7Qgd9B1KuMQ?t=2597
    friend void swap(Container<T>& a, Container<T>& b) noexcept
    {
        a.swap(b);
    }
 
    void clear() noexcept
    {
        //TODO: could be erase(begin(), end()).
        while (!empty()) {
            pop_back();
        }
    }
 
    void pop_back() noexcept
    {
        assert(!empty() && "pop_back() on empty container is undefined!");
        back().~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(), end(), 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;
    }
 
    void push_back(const T& value)
    {
        emplace_back(std::forward<const T&>(value));
    }
 
    reference at(size_type index)
    {
        if (index >= size()) {
            throw std::out_of_range("Container at() index is out of range!");
        }
        return _data[index];
    }
    const_reference at(size_type index) const
    {
        if (index >= size()) {
            throw std::out_of_range("Container at() index is out of range!");
        }
        return _data[index];
    }
 
    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;
    }
    reference front() noexcept
    {
        assert(!empty() && "front() on empty container is UB");
        return _data[0];
    }
    const_reference front() const noexcept
    {
        assert(!empty() && "front() on empty container is UB");
        return _data[0];
    }
    const_reference back() const noexcept
    {
        assert(!empty() && "back() on empty container is UB");
        return _data[size() - 1];
    }
    reference back() noexcept
    {
        assert(!empty() && "back() on empty container is UB");
        return _data[size() - 1];
    }
    bool empty() const noexcept
    {
        return size() == 0;
    }
    size_type capacity() const noexcept
    {
        return _capacity;
    }
    size_type size() const noexcept
    {
        return _count;
    }
        
    iterator begin() noexcept { return _data; }
    const_iterator begin() const noexcept { return _data; }
    const_iterator cbegin() const noexcept { return _data; }    
    const_iterator end() const noexcept { return _data + _count; }
    const_iterator cend() const noexcept { return _data + _count; }
    iterator end() noexcept { return _data + _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>
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);
}
}

Comments

  • Yuizcko icon
    05/18/26 12:59:20 PM UTC
    text |

    0 B

    |

    0 👍

    /

    0 👎

    Best t33n active cpx link daily updates!
     Copy and paste the link in a new browser tab and then hit enter!
     https://crazyporn.xxx/members/157354/?asgtbndr=1&play=true&spon=adworld&zone=400