How I can write a monotonic allocator in C++? - c++

I'm trying to write a very simple monotonic allocator that uses a fixed total memory size. Here is my code:
#include <map>
#include <array>
template <typename T, size_t SZ>
class monotonic_allocator
{
public:
using value_type = T;
monotonic_allocator() noexcept {}
[[nodiscard]]
value_type* allocate(std::size_t n)
{
size_t start = 0;
for (const auto& [alloc_start, alloc_size] : alloc_list_) {
if ((alloc_start - start) <= n) {
alloc_list_[start] = n;
return mem_.data() + start;
}
start = alloc_start + alloc_size;
}
throw std::bad_alloc{};
}
void deallocate(value_type* p, std::size_t n) noexcept
{
alloc_list_.erase(static_cast<size_t>(p - mem_.data()));
}
template <typename T1, size_t SZ1, typename T2, size_t SZ2>
friend bool operator==(monotonic_allocator<T1, SZ1> const& x, monotonic_allocator<T2, SZ2> const& y) noexcept;
private:
std::array<value_type, SZ> mem_;
std::map<size_t, size_t> alloc_list_{};
};
template <typename T1, size_t SZ1, typename T2, size_t SZ2>
bool operator==(monotonic_allocator<T1, SZ1> const& x, monotonic_allocator<T2, SZ2> const& y) noexcept
{
return SZ1 == SZ2 && x.mem_.data() == y.mem_.data();
}
template <typename T1, size_t SZ1, typename T2, size_t SZ2>
bool operator!=(monotonic_allocator<T1, SZ1> const& x, monotonic_allocator<T2, SZ2> const& y) noexcept
{
return !(x == y);
}
int main()
{
std::vector<int, monotonic_allocator<int, 4096>> vec = {1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,6};
}
But I get a strange error which says:
error: no type named 'type' in 'std::__allocator_traits_base::__rebind<monotonic_allocator<int, 4096>, int>'
Any idea how I can solve this problem? BTW, this code may have other problems too.

According to cppreference, rebind (which is part of the Allocator requirements) is only optional if your allocator is a template of the form <typename T, [possibly other type arguments]>. But your template is of the form <typename T, size_t N>, so it doesn't match (the size_t argument is a non-type argument).
So you have to add the rebind implementation yourself, like in the example in this question: How are allocator in C++ implemented?

Related

C++ custom template with unordered_map

I am trying to create a custom template, and have such code:
template <typename T>
struct Allocator {
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
T *allocate(size_type n, const void *hint=0);
T *allocate_at_least(size_type n);
void deallocate(T *p, size_type n);
};
template <class T, class U>
bool operator==(const Allocator<T>&, const Allocator<U>&) {
return true;
}
int main() {
using T = long int;
std::unordered_map<
T,
T,
std::hash<T>,
std::equal_to<T>,
Allocator< std::pair<const T, T> >
> a;
}
It works with vector, but it fails somewhere inside the templates when I use unordered_map.
Can you help me to figure out what I am doing wrong?
Here is the error:
error: no matching constructor for initialization of 'std::__detail::_Hashtable_alloc<Allocator<std::__detail::_Hash_node<std::pair<const long, long>, false>>>::__buckets_alloc_type' (aka 'Allocator<std::__detail::_Hash_node_base *>')
And link to code: https://godbolt.org/z/zje3EGjb6
P.S. If I replace Allocator to std::allocator everything works fine.
It needs to have a default constructor and a constructor that is like a copy constructor but parametrized on a non-T type. The following compiles.
template <typename T>
struct Allocator {
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
T* allocate(size_type n, const void* hint = 0) {
return nullptr;
}
T* allocate_at_least(size_type n) {
return nullptr;
}
void deallocate(T* p, size_type n) {
}
Allocator() {} // <= this
template <class U>
Allocator(const Allocator<U>&) {} // <= and this
};
template <class T, class U>
bool operator==(const Allocator<T>&, const Allocator<U>&) {
return true;
}
int main() {
using T = long int;
std::unordered_map<
T,
T,
std::hash<T>,
std::equal_to<T>,
Allocator< std::pair<const T, T> >
> a;
}

Custom iterator in range based for: issue with constness

The following code is based on that found in Modern C++ programming cookbook, and is compiled in VS 2017:
#include <iostream>
using namespace std;
template <typename T, size_t const Size>
class dummy_array
{
T data[Size] = {};
public:
T const & GetAt(size_t const index) const
{
if (index < Size) return data[index];
throw std::out_of_range("index out of range");
}
// I have added this
T & GetAt(size_t const index)
{
if (index < Size) return data[index];
throw std::out_of_range("index out of range");
}
void SetAt(size_t const index, T const & value)
{
if (index < Size) data[index] = value;
else throw std::out_of_range("index out of range");
}
size_t GetSize() const { return Size; }
};
template <typename T, typename C, size_t const Size>
class dummy_array_iterator_type
{
public:
dummy_array_iterator_type(C& collection,
size_t const index) :
index(index), collection(collection)
{ }
bool operator!= (dummy_array_iterator_type const & other) const
{
return index != other.index;
}
T const & operator* () const
{
return collection.GetAt(index);
}
// I have added this
T & operator* ()
{
return collection.GetAt(index);
}
dummy_array_iterator_type const & operator++ ()
{
++index;
return *this;
}
private:
size_t index;
C& collection;
};
template <typename T, size_t const Size>
using dummy_array_iterator = dummy_array_iterator_type<T, dummy_array<T, Size>, Size>;
// I have added the const in 'const dummy_array_iterator_type'
template <typename T, size_t const Size>
using dummy_array_const_iterator = const dummy_array_iterator_type<T, dummy_array<T, Size> const, Size>;
template <typename T, size_t const Size>
inline dummy_array_iterator<T, Size> begin(dummy_array<T, Size>& collection)
{
return dummy_array_iterator<T, Size>(collection, 0);
}
template <typename T, size_t const Size>
inline dummy_array_iterator<T, Size> end(dummy_array<T, Size>& collection)
{
return dummy_array_iterator<T, Size>(collection, collection.GetSize());
}
template <typename T, size_t const Size>
inline dummy_array_const_iterator<T, Size> begin(dummy_array<T, Size> const & collection)
{
return dummy_array_const_iterator<T, Size>(collection, 0);
}
template <typename T, size_t const Size>
inline dummy_array_const_iterator<T, Size> end(dummy_array<T, Size> const & collection)
{
return dummy_array_const_iterator<T, Size>(collection, collection.GetSize());
}
int main(int nArgc, char** argv)
{
dummy_array<int, 10> arr;
for (auto&& e : arr)
{
std::cout << e << std::endl;
e = 100; // PROBLEM
}
const dummy_array<int, 10> arr2;
for (auto&& e : arr2) // ERROR HERE
{
std::cout << e << std::endl;
}
}
Now, the error is pointing at the line
T & operator* ()
stating
'return': cannot convert from 'const T' to 'T &'"
...which is raised from my range based for loop on arr2.
Why is the compiler choosing the none-constant version of operator*()?. I have looked at this for a long time; I think its because it thinks that the object on which it is calling this operator is not constant: this should be a dummy_array_const_iterator. But, this object has been declared to be constant via
template <typename T, size_t const Size>
using dummy_array_const_iterator = const dummy_array_iterator_type<T, dummy_array<T, Size> const, Size>;
...so I really don't understand what is happening. Can someone please clarify?
TIA
dummy_array_const_iterator::operator * should always return T const & regardless of constness of the iterator object itself.
The easiest way to achieve this is probably just to declare it with T const as underlying iterator value type:
template <typename T, size_t const Size>
using dummy_array_const_iterator = dummy_array_iterator_type<T const, dummy_array<T, Size> const, Size>;
Since you are returning the iterator by value its constness can be easily lost by c++ type deduction rules and just declaring dummy_array_const_iterator as alias to const dummy_array_iterator_type is not enough. i.e. the following fails:
#include <type_traits>
struct I { };
using C = I const;
C begin();
int bar()
{
auto x = begin(); // type of x is deduced as I
static_assert(std::is_same<I, decltype(x)>::value, "same"); // PASS
static_assert(std::is_same<decltype(begin()), decltype(x)>::value, "same"); // ERROR
}
I found a way to enable T& operator*() only when C is not constant:
template <class Tp = T>
typename std::enable_if<std::is_const<C>::value, Tp>::type const& operator* () const
{
return collection.GetAt(index);
}
template <class Tp = T>
typename std::enable_if<!std::is_const<C>::value, Tp>::type & operator* () const
{
return collection.GetAt(index);
}
I have no idea about the syntax (which I get from https://stackoverflow.com/a/26678178)

c++ Force implicit conversion on pass as argument

I have problem with implicit conversions in C++.
I'm trying to create some Expression template for vector arithmetics (I know that same libraries already exists. I'm just learning C++ so I wanted to try something with templates).
I would like to create class Vector, that is able to compute like this:
simd::test::Vector<char, 5> a;
simd::test::Vector<short, 5> b;
auto ret = a + b + a + b;
, where on output would be Vector of shorts becouse short is bigger type than char.
Right now, I have class that is able to adds vectors of same data types. For different types I have to call explicit conversion:
//simd::test::Vector<short, 5>(a)
auto ret = simd::test::Vector<short, 5>(a) + b + simd::test::Vector<short, 5>(a) + b;
Is possible to implicit convert Vector before pass into function "operator+()"? Here is my code of Vector:
#pragma once
#include <type_traits>
namespace simd {
namespace test {
template<typename R, std::size_t Dim,
typename std::enable_if<std::is_arithmetic<R>::value>::type* = nullptr
>
class Vector_expression {
public:
static constexpr std::size_t size = Dim;
virtual const R operator[] (std::size_t index) const = 0;
virtual ~Vector_expression() = default;
};
template<typename T, std::size_t Dim>
class Vector final : public Vector_expression<T, Dim> {
private:
T data[Dim];
public:
Vector() = default;
template<typename R>
Vector(const Vector_expression<R, Dim> &obj) {
for(std::size_t index = 0; index < Dim; ++index) {
data[index] = obj[index];
}
}
template<typename R>
Vector(Vector_expression<R, Dim> &&obj) {
for(std::size_t index = 0; index < Dim; ++index) {
data[index] = obj[index];
}
}
template<typename R>
Vector<T, Dim> & operator=(const Vector_expression<R, Dim> &obj) {
for(std::size_t index = 0; index < Dim; ++index) {
data[index] = obj[index];
}
return (*this);
}
template<typename R>
Vector<T, Dim> & operator=(Vector_expression<R, Dim> && obj) {
for(std::size_t index = 0; index < Dim; ++index) {
data[index] = obj[index];
}
return (*this);
}
virtual const T operator[] (std::size_t index) const override {
return data[index];
}
T & operator[] (std::size_t index) {
return data[index];
}
virtual ~Vector() = default;
};
template<typename E1, typename E2, typename R, std::size_t Dim>
class Vector_sum final : public Vector_expression<R, Dim> {
private:
const E1 & _lhs;
const E2 & _rhs;
public:
Vector_sum() = delete;
Vector_sum(const E1 & lhs, const E2 & rhs) :
_lhs(lhs),
_rhs(rhs)
{}
virtual const R operator[] (std::size_t index) const override {
return _lhs[index] + _rhs[index];
}
virtual ~Vector_sum() = default;
};
template<typename R, std::size_t Dim>
Vector_sum<Vector_expression<R, Dim>, Vector_expression<R, Dim>, R, Dim> operator+ (const Vector_expression<R, Dim> & lhs, const Vector_expression<R, Dim> & rhs) {
return {lhs, rhs};
}
}
}
Just define an operator+ that allows different argument types. The one catch is determining the element type of the resulting sum. Probably the best option is to use whatever the result of adding two elements is. One way to write this type is:
decltype(std::declval<const R1>() + std::declval<const R2>())
Or if you know the types are built-in arithmetic types, that would be the same as
std::common_type_t<R1, R2>
Or using a trailing return type, we can take advantage of the function parameters to shorten the std::declval expressions:
template<typename R1, typename R2, std::size_t Dim>
auto operator+ (const Vector_expression<R1, Dim> & lhs,
const Vector_expression<R2, Dim> & rhs)
-> Vector_sum<Vector_expression<R1, Dim>, Vector_expression<R2, Dim>,
decltype(lhs[0] + rhs[0]), Dim>
{
return {lhs, rhs};
}
It could be done using templates and std::common_type, something like this:
template<typename T1, typename T2, size_t S>
simd::test::Vector<typename std::common_type<T1, T2>::type, S>
operator+(simd::test::Vector<T1, S> const& v1,
simd::test::Vector<T2, S> const& v2)
{
// TODO: Implementation...
}

Custom allocator only compiles in Release mode in VS 2015

I wrote 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();
}
However, only in Debug Mode in VS2015, I get the following compiler error:
'std::StackAllocator<T2, std::allocator<T>>::StackAllocator(std::StackAllocator<T, std::allocator<T>> &&)':
cannot convert argument 1 from
'std::_Wrap_alloc<std::StackAllocator<int,std::allocator<T>>>'
to
'const std::allocator<T>&'
in "c:\program files (x86)\microsoft visual studio 14.0\vc\include\xmemory0" at line 952
Compilation and execution work as intended in Release mode, though.
Here is stack_allocator.h:
#pragma once
#include <functional>
namespace std {
template <typename T, typename Allocator = 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 {
return (!(less<const_pointer>()(p, m_begin)) && (less<const_pointer>()(p, m_end)));
}
public:
StackAllocator(const Allocator& alloc = Allocator()) noexcept :
m_size(0),
m_allocator(alloc),
m_begin(nullptr),
m_end(nullptr),
m_stack_pointer(nullptr) {
}
StackAllocator(pointer buffer, size_t size, const Allocator& alloc = Allocator()) noexcept :
m_size(size),
m_allocator(alloc),
m_begin(buffer),
m_end(buffer + size),
m_stack_pointer(buffer) {
}
template <typename T2>
StackAllocator(const StackAllocator<T2, Allocator>& other) noexcept :
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) {
}
pointer allocate(size_type n, const_void_pointer hint = const_void_pointer()) {
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);
}
void deallocate(pointer p, size_type n) {
if (pointer_to_internal_buffer(p))
m_stack_pointer -= n;
else
m_allocator.deallocate(p, n);
}
size_type capacity() const noexcept {
return m_size;
}
size_type max_size() const noexcept {
return m_size;
}
pointer address(reference x) const noexcept {
if (pointer_to_internal_buffer(addressof(x)))
return addressof(x);
else
return m_allocator.address(x);
}
const_pointer address(const_reference x) const noexcept {
if (pointer_to_internal_buffer(addressof(x)))
return addressof(x);
else
return m_allocator.address(x);
}
pointer buffer() const noexcept {
return m_begin;
}
template <typename T2, typename... Args>
void construct(T2* p, Args&&... args) {
m_allocator.construct(p, forward<Args>(args)...);
}
template <typename T2>
void destroy(T2* p) {
m_allocator.destroy(p);
}
template <typename T2>
bool operator==(const StackAllocator<T2, Allocator>& other) const noexcept {
return buffer() == other.buffer();
}
template <typename T2>
bool operator!=(const StackAllocator<T2, Allocator>& other) const noexcept {
return buffer() != other.buffer();
}
};
}
Anybody has a clue as to why this error is occurring? How do I solve it?
Your rebind is broken, it should be:
template<typename T2>
struct rebind {
using Alloc2
= typename allocator_traits<Allocator>::rebind_alloc<T2>;
using other = StackAllocator<T2, Alloc2>;
};
Otherwise rebinding always creates something using std::allocator<T2> not something related to the current Allocator argument.
e.g. if you instantiate StackAllocator<int, SomeAlloc<int> and then rebind it to long you get StackAllocator<long, std::allocator<long>> which is a completely different type.
I think the VC++ debug mode is creating some kind of wrapper allocator by rebinding yours, and it fails because of your broken rebind.
Also, these lines are a problem:
typedef typename Allocator::reference reference;
typedef typename Allocator::const_reference const_reference;
Types meeting the allocator requirements don't have to have reference and const_reference so by adding these typedefs you ensure your allocator can only work with a subset of allocators. If you think you need them, just define them the same way std::allocator does:
typedef value_type& reference;
typedef const value_type& const_reference;

Tag dispatching/enable_if - I am confused

I have the following structs:
struct A
{
}
struct B
{
tuple<string,string> children{{"test1","test2"}};
}
I would like to create a template function that will overload the << operator on every class having a member variable called children.
If possible- only on tuples named children.
When a class with a children tuple like B is met it should iterate the tuple members and call << on each of them.
Something like:
template<typename RECEIVERTYPE,typename SENDERTYPE>
typename std::enable_if<std::have_children_member<RECEIVER_TYPE>::value, void>::type
RECEIVERTYPE& operator<< (RECEIVERTYPE& streamReceiver, const SENDERTYPE& streamSender)
{
for_each(streamSender.children, [&](const auto& child)
{
streamReceiver << child;
});
return streamReceiver;
}
I have tried alot of examples- but I cant really get anything working in visual studio 2015.
I would just do this to only match types with a tuple member called children:
template<typename S, typename T,
std::size_t = std::tuple_size<decltype(T::children)>::value>
S& operator<<(S& s, const T& t)
{ ... }
And might implement the body like this:
template<typename S, typename T, std::size_t... I>
void
print_tuple_like(S& s, const T& t, std::index_sequence<I...>)
{
void* unused[] = { &(s << std::get<I>(t))... };
}
template<typename S, typename T,
std::size_t N = std::tuple_size<decltype(T::children)>::value>
S& operator<<(S& s, const T& t)
{
print_tuple_like(s, t.children, std::make_index_sequence<N>{});
return s;
}
Or like:
template<std::size_t N, typename S, typename T>
void
print_tuple_like(S& s, const T& t, std::false_type)
{ }
template<std::size_t N, typename S, typename T>
void
print_tuple_like(S& s, const T& t, std::true_type)
{
s << std::get<N>(t);
print_tuple_like<N+1>(s, t, std::integral_constant<bool, (N+1 < std::tuple_size<T>::value)>{});
}
template<typename S, typename T,
std::size_t N = std::tuple_size<decltype(T::children)>::value>
S& operator<<(S& s, const T& t)
{
print_tuple_like<0>(s, t.children, std::integral_constant<bool, (N != 0)>{});
return s;
}