Почему мой C ++ 11 совместим?Ошибка линейного распределителя во время выполнения? - PullRequest
0 голосов
/ 28 сентября 2019

Линейный распределитель:

#pragma once

#include <cstddef>
#include <cassert>
#include <new>
#include <algorithm>
#include "aligned_allocations.hpp"
#include <utility>

#include <iostream>

template <typename T, std::size_t alignment = alignof(std::max_align_t)>
class LinearAllocator
{
    T* m_pstart;
    std::size_t m_offset;
    std::size_t m_maxSize;
public:
    using value_type = T;
    using pointer = T*;
    using propagate_on_container_copy_assignment    = std::true_type;
    using propagate_on_container_move_assignment    = std::true_type;
    using propagate_on_container_swap               = std::true_type;

    LinearAllocator() = default;

    LinearAllocator(const std::size_t totalSize) noexcept
        : m_pstart{ nullptr },
        m_offset{ 0 },
        m_maxSize{ totalSize }
    {
        static_assert(isPowerOfTwo(alignment), "Alignment value must be a power of 2.");
        init();
    }

    ~LinearAllocator() noexcept
    {
        reset();
        alignedFree(m_pstart);
        m_pstart = nullptr;
    }

    void init()
    {
        if (m_pstart != nullptr)
        {
            reset();
            alignedFree(m_pstart);
        }
        m_pstart = static_cast<T*>(alignedMalloc(m_maxSize, getAlignment()));
        assert(isAligned(m_pstart, getAlignment()));
    }

    void reset() noexcept
    {
        m_offset = 0;
    }

    template<typename Other, std::size_t OtherAlignment = alignof(std::max_align_t)>
    struct rebind {
        using other = LinearAllocator<Other, OtherAlignment>;
    };

    LinearAllocator(LinearAllocator& rhs) noexcept
        : m_pstart{ static_cast<T*>(alignedMalloc(rhs.getMaxSize(), getAlignment())) },
        m_offset{ rhs.getOffset() },
        m_maxSize{ rhs.getMaxSize() }
    {
        assert(isAligned(m_pstart, getAlignment()));
    }
    template<typename Other, std::size_t OtherAlignment>
    LinearAllocator(const LinearAllocator<Other, OtherAlignment>& rhs) noexcept
        : m_pstart{ static_cast<T*>(alignedMalloc(rhs.getMaxSize(), OtherAlignment)) },
        m_offset{ rhs.getOffset() },
        m_maxSize{ rhs.getMaxSize() }
    {
        assert(isAligned(m_pstart, OtherAlignment));
    }
    template<typename Other, std::size_t OtherAlignment>
    LinearAllocator& operator=(LinearAllocator<Other, OtherAlignment>& rhs) noexcept
    {
        if (this != &rhs)
        {
            m_pstart = static_cast<T*>(alignedMalloc(rhs.getMaxSize(), OtherAlignment));
            m_offset = rhs.getOffset();
            m_maxSize = rhs.getMaxSize();
            assert(isAligned(m_pstart, OtherAlignment));
        }
        return *this;
    }
    LinearAllocator(LinearAllocator&& rhs) noexcept
        : m_pstart{ rhs.getStartAddress() },
        m_offset{ rhs.getOffset() },
        m_maxSize{ rhs.getMaxSize() }
    {
        assert(isAligned(m_pstart, getAlignment()));
    }
    template<typename Other, std::size_t OtherAlignment>
    LinearAllocator(LinearAllocator<Other, OtherAlignment>&& rhs) noexcept
        : m_pstart{ rhs.getStartAddress() },
        m_offset{ rhs.getOffset() },
        m_maxSize{ rhs.getMaxSize() }
    {
        assert(isAligned(m_pstart, OtherAlignment));
    }
    template<typename Other, std::size_t OtherAlignment>
    LinearAllocator& operator=(LinearAllocator<Other, OtherAlignment>&& rhs) noexcept
    {
        if (this != &rhs)
        {
            m_pstart = rhs.getStartAddress();
            m_offset = rhs.getOffset();
            m_maxSize = rhs.getMaxSize();
            assert(isAligned(m_pstart, OtherAlignment));
        }
        return *this;
    }

    [[nodiscard]]
    T* allocate(const std::size_t bytes)
    {
        assert(m_pstart != nullptr);
        std::size_t currentAddress = getCurrentAddress();
        std::size_t userPtr = reinterpret_cast<std::size_t>(alignForward(
                        reinterpret_cast<T*>(currentAddress + bytes), getAlignment()));
        assert(isAligned(userPtr, getAlignment()));

        // check if there is enough memory available
        if (userPtr > getEndAddress())
        {
            throw std::bad_alloc{};
        }
        m_offset += userPtr - currentAddress;

        return reinterpret_cast<T*>(currentAddress);
    }

    void deallocate([[maybe_unused]] T* p, [[maybe_unused]] const std::size_t count) noexcept
    {
        assert(false);
        reset();
    }

    //[[deprecated("construct() is deprecated in C++17 and will be removed in C++20")]]
    template<typename J, typename... Args>
    void construct(J* p, Args&&... args) const
    {
        new(p) J(std::forward<Args>(args)...);
    }

    template<typename J>
    void destroy(J* p) const noexcept
    {
        p->~J();
    }

    template<typename... Args>
    void construct(T* const p, Args&&... args) const
    {
        new(p) T(std::forward<Args>(args)...);
    }

    void destroy(T* const p) const noexcept
    {
        p->~T();
    }

    /// \brief HELPERS
    inline std::size_t getAvailableMemory() const noexcept {
        return getEndAddress() - getCurrentAddress();
    }
    inline std::size_t getCurrentAddress() const noexcept {
        return reinterpret_cast<std::size_t>(m_pstart) + m_offset;
    }
    inline std::size_t getEndAddress() const noexcept {
        return reinterpret_cast<std::size_t>(m_pstart) + m_maxSize;
    }
    inline constexpr std::size_t getAlignment() const noexcept {
        return (alignment > sizeof(void*)) ? alignment : sizeof(void*);
    }

    /// \brief GETTERS
    inline T* getStartAddress() const noexcept {
        return m_pstart;
    }
    inline std::size_t getOffset() const noexcept {
        return m_offset;
    }
    inline std::size_t getMaxSize() const noexcept {
        return m_maxSize;
    }

};

template<typename T, std::size_t alignment1, typename Other, std::size_t alignment2>
inline bool operator==(const LinearAllocator<T, alignment1>& lhs, const LinearAllocator<Other, alignment2>& rhs) noexcept
{
    return lhs.getStartAddress() == rhs.getStartAddress();
}
template<typename T, std::size_t alignment1, typename Other, std::size_t alignment2>
inline bool operator!=(const LinearAllocator<T, alignment1>& lhs, const LinearAllocator<Other, alignment2>& rhs) noexcept
{
    return lhs.getStartAddress() != rhs.getStartAddress();
}

тесты:

#include "linear_allocator.hpp"
#include <deque>
#include <iostream>
#include <string>
#include <typeinfo>
#include <utility>


int main()
{
    std::cout << std::boolalpha << '\n';

    std::cout << "Individual allocations" << '\n';
    [[maybe_unused]]
    LinearAllocator<int, 256> la{ 1024 };
    std::cout << la.getStartAddress() << '\n';
    std::cout << la.getCurrentAddress() << '\n';
    std::cout << la.getAvailableMemory() << '\n';
    std::cout << la.getMaxSize() << '\n';
    std::cout << la.getAlignment() << '\n';

    std::cout << "int fundamental type example" << '\n';
    void* vi = la.allocate(sizeof(int));
    int* pi = new(vi) int{ 4 };
    std::cout << *pi << '\n';
    std::cout << "isAligned(pi, 256)=" << isAligned(pi, 256) << '\n';

    int* pint = la.allocate(sizeof(int));
    *pint = 1453;
    std::cout << *pint << '\n';
    std::cout << "isAligned(pint, 256)=" << isAligned(pint, 256) << '\n';


    std::cout << "\nstd::string example" << '\n';

    std::cout << "\nAlign each element of an array:" << '\n';
    constexpr std::size_t intAlignment = 16;
    struct AlignedInt {
        alignas(intAlignment) int aint;
    };
    LinearAllocator<AlignedInt, intAlignment> LA{ sizeof(AlignedInt) * intAlignment * 4 * 64 };
    AlignedInt* palignedInts = LA.allocate(sizeof(AlignedInt) * 64);
    for (int i = 0; i < 64; i++)
        (palignedInts + sizeof(AlignedInt) * i)->aint = i;
    for (int i = 0; i < 64; i++)
        std::cout << (palignedInts + sizeof(AlignedInt) * i)->aint << ' ' << isAligned((palignedInts + sizeof(AlignedInt) * i), intAlignment) << '\n';
    // it's aligned!

    // everything up to this point works

    // FAILS:
    using lstring = std::basic_string<char, std::char_traits<char>, std::allocator<char>>;
    LinearAllocator<std::string, 512> sla{ 789430 };
    std::string* ps = sla.allocate(8192);
    *ps = "My name is Maximus Decimus Meridius.\n"; // fails here
    std::cout << *ps << '\n';

    // FAILS:
    LinearAllocator<char, 16> LC{ sizeof(char) * 16 * 1024 };
    std::deque<char, decltype(LC)> dq{ 100, LC }; // fails here
    assert(isAligned(&dq, 16));
    dq.push_front('r');
    dq.push_front('e');
    dq.push_front('m');
    dq.push_front('e');
    dq.push_front('m');
    dq.push_front('b');
    dq.push_front('e');
    dq.push_front('r');
    dq.push_front('m');
    dq.push_front('e');
    for (const auto &i : dq)
    {
        std::cout << i << '\n';
    }
    std::cout << typeid(dq.get_allocator()).name() << '\n';

}

Как вы можете видеть, он выходит из строя, когда я поставляю ему std::queue, он также отказывает, если я его поставляюstd::string.Это работает только для фундаментальных типов и типов, которые я создаю, которые не нуждаются в распределителе.Вы можете видеть это в тестах.Я использую последнюю версию MSVS 2017, используя C ++ 17.Эта ошибка во время выполнения явно заставляет меня поверить, что есть какое-то требование к распределителю, которое я не выполняю, и поэтому мой распределитель остается несовместимым со стандартными контейнерами библиотеки и другими средствами.

Кстати, я знаю, что многие изфункции, которые я предоставил в распределителе, не нужны, так как они поставляются allocator_traits.Тем не менее, я добавил их на тот случай, если проблема была глубже внутри библиотеки, но я всегда получаю одну и ту же ошибку.Я отладил, и упоминаются строки, где это терпит неудачу - 2 строки, 1 для std::string 1 для std::queue.

Я не могу найти, в чем проблема.Компилятор также не имеет претензий.Может кто-нибудь помочь?

PS.

Еще одна вещь, которая сейчас может быть неактуальной.Структура rebind предположительно не нужна, поскольку она предоставляется allocator_traits.Однако мой распределитель имеет 2 параметра шаблона, и я не думаю, что за это отвечает allocator_traits, он учитывает только 1 параметр шаблона T.Если я либо удалю rebind, либо сделаю его второй параметр шаблона параметром не по умолчанию (по умолчанию alignof(std::max_align_it) сейчас), появится много ошибок, например: Severity Code Description Project File Line Suppression State Error C2027 use of undefined type 'std::_Replace_first_parameter<_Other,_Ty>' Allocators c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\xmemory0 553 и Severity Code Description Project File Line Suppression State Error C2061 syntax error: identifier 'type' Allocators c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\xmemory0 553 , Severity Code Description Project File Line Suppression State Error C2903 '_Is_simple_alloc_v': symbol is neither a class template nor a function template Allocators c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\deque 652 и еще 20..

...