1

I'm trying to write a simple dummy allocator for vector<> so that I can use vector<> as a wrapper for stack arrays, like so:

#include <vector>
#include "stdio.h"
#include "stack_allocator.h"

using namespace std;

int main() {
    int buffer[100];
    vector<int, StackAllocator<int>> v((StackAllocator<int>(buffer, 100)));
    v.push_back(2);
    printf("%d", v[0]);
    v.pop_back();
}

I wrote the StackAllocator class, but I'm getting linker errors in Visual Studio which are unfortunately quite generic:

1> main.cpp
1> main.obj: error LNK2001: unresolved external symbol "public: void __cdecl std::StackAllocator<int,class std::allocator<int> >::destroy<int>(int *)" (??$destroy@H@?$StackAllocator@HV?$allocator@H@std@@@std@@QEAAXPEAH@Z)
1> main.obj: error LNK2001: unresolved external symbol "public: void __cdecl std::StackAllocator<int,class std::allocator<int> >::construct<int,int>(int *,int &&)" (??$construct@HH@?$StackAllocator@HV?$allocator@H@std@@@std@@QEAAXPEAH$$QEAH@Z)
1> main.obj: error LNK2001: unresolved external symbol "public: unsigned __int64 __cdecl std::StackAllocator<int,class std::allocator<int> >::max_size(void)const " (?max_size@?$StackAllocator@HV?$allocator@H@std@@@std@@QEBA_KXZ)
1> main.obj: error LNK2001: unresolved external symbol "public: void __cdecl std::StackAllocator<int,class std::allocator<int> >::deallocate(int *,unsigned __int64)" (?deallocate@?$StackAllocator@HV?$allocator@H@std@@@std@@QEAAXPEAH_K@Z)
1> main.obj: error LNK2001: unresolved external symbol "public: int * __cdecl std::StackAllocator<int,class std::allocator<int> >::allocate(unsigned __int64,void const *)" (?allocate@?$StackAllocator@HV?$allocator@H@std@@@std@@QEAAPEAH_KPEBX@Z)
1> main.obj: error LNK2001: unresolved external symbol "public: __cdecl std::StackAllocator<int,class std::allocator<int> >::StackAllocator<int,class std::allocator<int> >(int *,unsigned __int64,class std::allocator<int> const &)" (??0?$StackAllocator@HV?$allocator@H@std@@@std@@QEAA@PEAH_KAEBV?$allocator@H@1@@Z)
1> C:\Users\mathu\Desktop\StackVector\x64\Release\StackVector.exe : fatal error LNK1120: 6 unresolved externals

Here is my code:

stack_allocator.h:

#pragma once

#include <functional>

namespace std {
    template <typename T, typename Allocator = std::allocator<T>>
    class StackAllocator {
    public:
        typedef typename allocator_traits<Allocator>::value_type value_type;
        typedef typename allocator_traits<Allocator>::pointer pointer;
        typedef typename allocator_traits<Allocator>::const_pointer const_pointer;
        typedef typename allocator_traits<Allocator>::size_type size_type;
        typedef typename allocator_traits<Allocator>::difference_type difference_type;
        typedef typename allocator_traits<Allocator>::const_void_pointer const_void_pointer;
        typedef typename Allocator::reference reference;
        typedef typename Allocator::const_reference const_reference;

        template<typename T2>
        struct rebind {
            typedef StackAllocator<T2> other;
        };

    private:
        size_t m_size;
        Allocator m_allocator;
        pointer m_begin;
        pointer m_end;
        pointer m_stack_pointer;

        bool pointer_to_internal_buffer(const_pointer p) const;

    public:
        StackAllocator(const Allocator& alloc = Allocator());
        StackAllocator(pointer buffer, size_t size, const Allocator& alloc = Allocator());
        template <typename T2>
        StackAllocator(const StackAllocator<T2, Allocator>& other);
        pointer allocate(size_type n, const_void_pointer hint = const_void_pointer());
        void deallocate(pointer p, size_type n);
        size_type capacity() const;
        size_type max_size() const noexcept;
        pointer address(reference x) const noexcept;
        const_pointer address(const_reference x) const noexcept;
        pointer buffer() const noexcept;

        template <typename T2, typename... Args>
        void construct(T2* p, Args&&... args);

        template <typename T2>
        void destroy(T2* p);

        template <typename T2>
        bool operator==(const StackAllocator<T2, Allocator>& other) const noexcept;

        template <typename T2>
        bool operator!=(const StackAllocator<T2, Allocator>& other) const noexcept;
    };
}

stack_allocator.cpp:

#include "stack_allocator.h"

namespace std {
#define AllocatorTemplate template <typename T, typename Allocator>
#define StackAlloc StackAllocator<T, Allocator>

    AllocatorTemplate StackAlloc::StackAllocator(const Allocator& alloc) :
        m_size(0),
        m_allocator(alloc),
        m_begin(nullptr),
        m_end(nullptr),
        m_stack_pointer(nullptr) {
    }

    AllocatorTemplate StackAlloc::StackAllocator(pointer buffer, size_t size, const Allocator& alloc) :
        m_size(size),
        m_allocator(alloc),
        m_begin(buffer),
        m_end(buffer + size),
        m_stack_pointer(buffer) {
    }

    AllocatorTemplate template <typename T2> StackAlloc::StackAllocator(const StackAllocator<T2, Allocator>& other) :
        m_size(other.m_size),
        m_allocator(other.m_allocator),
        m_begin(other.m_begin),
        m_end(other.m_end),
        m_stack_pointer(other.m_stack_pointer) {
    }

    AllocatorTemplate typename StackAlloc::size_type StackAlloc::capacity() const {
        return m_size;
    }

    AllocatorTemplate typename StackAlloc::pointer StackAlloc::allocate(size_type n, const_void_pointer hint) {
        if (n <= size_type(distance(m_stack_pointer, m_end))) {
            pointer result = m_stack_pointer;
            m_stack_pointer += n;
            return result;
        }
        else
            return m_allocator.allocate(n, hint);
    }

    AllocatorTemplate void StackAlloc::deallocate(pointer p, size_type n) {
        if (pointer_to_internal_buffer(p))
            m_stack_pointer -= n;
        else
            m_allocator.deallocate(p, n);
    }

    AllocatorTemplate typename StackAlloc::size_type StackAlloc::max_size() const noexcept {
        return m_size();
    }

    AllocatorTemplate template <typename T2, typename... Args> void StackAlloc::construct(T2* p, Args&&... args) {
        m_allocator.construct(p, forward<Args>(args)...);
    }

    AllocatorTemplate template <typename T2> void StackAlloc::destroy(T2* p) {
        m_allocator.destroy(p);
    }

    AllocatorTemplate typename StackAlloc::pointer StackAlloc::address(reference x) const noexcept {
        if (pointer_to_internal_buffer(addressof(x)))
            return addressof(x);
        else
            return m_allocator.address(x);
    }

    AllocatorTemplate typename StackAlloc::const_pointer StackAlloc::address(const_reference x) const noexcept {
        if (pointer_to_internal_buffer(addressof(x)))
            return addressof(x);
        else
            return m_allocator.address(x);
    }

    AllocatorTemplate typename StackAlloc::pointer StackAlloc::buffer() const noexcept {
        return m_begin;
    }

    AllocatorTemplate bool StackAlloc::pointer_to_internal_buffer(const_pointer p) const {
        return (!(less<const_pointer>()(p, m_begin)) && (less<const_pointer>()(p, m_end)));
    }

    AllocatorTemplate template <typename T2> bool StackAlloc::operator==(const StackAllocator<T2, Allocator>& other) const noexcept {
        return buffer() == other.buffer();
    }

    AllocatorTemplate template <typename T2> bool StackAlloc::operator!=(const StackAllocator<T2, Allocator>& other) const noexcept {
        return buffer() != other.buffer();
    }
}

I apologise for posting a lot of code, but I genuinely have no idea where the issue might be. Anybody can spot what's wrong?

MathuSum Mut
  • 2,765
  • 3
  • 27
  • 59
  • While it's an interesting exercise, I wonder if there's any advantage in doing this compared to just using `std::array`. – Christian Hackl Feb 11 '17 at 12:43
  • There is, as after I get it working I plan to make it support variable length arrays created on the stack using alloca(), that's why I avoided making the size a compile-time constant. – MathuSum Mut Feb 11 '17 at 13:19
  • VLAs are supported by GCC in C++ as an extension. They are not standard C++. – Christian Hackl Feb 11 '17 at 13:40
  • Exactly! ;) Visual Studio does not support VLAs, so I can use alloca() to allocate a variable length buffer on the stack and use this class to wrap it with a `vector<>` instance. – MathuSum Mut Feb 11 '17 at 13:43
  • `alloca` is not standard C++, either. See also http://stackoverflow.com/questions/1018853/why-is-the-use-of-alloca-not-considered-good-practice – Christian Hackl Feb 11 '17 at 14:11
  • I am aware, but it is far more supported than C++ VLAs. Following standard C++ is not my intention with this project. – MathuSum Mut Feb 11 '17 at 14:14

1 Answers1

1

Because the Template Classes can be only implemented in the header file. Read this: Why can templates only be implemented in the header file?

Community
  • 1
  • 1
JustRufus
  • 492
  • 1
  • 5
  • 10