std::end for unique_ptr<T[]> - c++

I want to implement std::end for unique pointer.
The problem is that I have to get N(count of elements in array).
1.Approach deduce type from template
template <typename T, size_t N>
T* end(const unique_ptr<T[N]> &arr)
{
return arr.get() + N;
}
But I got error error: C2893: Failed to specialize function template 'T *test::end(const std::unique_ptr> &)' with [ _Ty=T [N] ] With the following template arguments: 'T=int' 'N=0x00'
It looks like It is not possible to deduce N
2.Get N from allocator.
Allocator has to know N to correctly execute delete[].
You could read about this in this article. There are two approaches:
Over-allocate the array and put n just to the left.
Use an associative array with p as the key and n as the value.
The problem is how to get this size cross platform/compiler.
Maybe someone knows better approaches or know how to make this works?

If you have a run time sized array and you need to know the size of it without having to manually do the book keeping then you should use a std::vector. It will manage the memory and size for you.
std::unique_ptr<T[]> is just a wrapper for a raw pointer. You cannot get the size of the block the pointer points to from just the pointer. The reason you use a std::unique_ptr<T[]> over T* foo = new T[size] is the unique_ptr makes sure delete[] is called when the pointer goes out of scope.

Something like this?
template<class X>
struct sized_unique_buffer;
template<class T, std::size_t N>
struct sized_unique_buffer<T[N]>:
std::unique_ptr<T[]>
{
using std::unique_ptr<T[]>::unique_ptr;
T* begin() const { return this->get(); }
T* end() const { return *this?begin(*this)+N:nullptr; }
bool empty() const { return N==0 || !*this; }
};
where we have a compile-time unenforced promise of a fixed compile-time length.
A similar design could work for a dynamic runtime length.
In some compilers, the number of T when T can be trivially destroyed is not stored when you call new T[N]. The system is free to over-allocate and give you a larger buffer (ie, round to a page boundary for a large allocation, or implicitly store the size of the buffer via the location from which it is allocated to reduce overhead and round allocations up), so the allocation size need not exactly match the number of elements.
For non-trivially destroyed T it is true that the compiler must know how many to destroy from just the pointer. This information is not exposed to C++.
You can do manual allocation of buffers and the count and pass that on to a unique_ptr with a custom deleter, even a stateless one. This would permit a type
unique_buffer<T[]> ptr;
where you can get the number of elements out at only a modest runtime cost.
If you instead store the length in the deleter, you can get a bit more locality on the loop limits (saving a cache miss) at the cost of a larger unique_buffer<T[]>.
Doing this with an unadulterated unique_ptr<T[]> is not possible in a portable way.

Related

Custom allocator with preallocated memory for STL containers

I'd like to use a std::vector which allocates the memory for its element from a preallocated buffer. So, I would like to provide a buffer pointer T* buffer of size n to std::vector.
I thought I could simply write a std::span-like class which also provides a push_back method; that would be exactly what I need. However, I've stumbled across the code from this post (see below) which seems to solve this problem with a custom allocator.
Nobody commented on that, but doesn't the example with std::vector<int, PreAllocator<int>> my_vec(PreAllocator<int>(&my_arr[0], 100)); provided in the post end in undefined behavior? I've run the code with the Visual Studio 2019 implementation and at least this implementation is rebinding the provided allocator to allocate an element of type struct std::_Container_proxy. Now this should be a huge problem, since you've only provided memory to store your 100 int's. Am I missing something?
template <typename T>
class PreAllocator
{
private:
T* memory_ptr;
std::size_t memory_size;
public:
typedef std::size_t size_type;
typedef T* pointer;
typedef T value_type;
PreAllocator(T* memory_ptr, std::size_t memory_size) : memory_ptr(memory_ptr), memory_size(memory_size) {}
PreAllocator(const PreAllocator& other) throw() : memory_ptr(other.memory_ptr), memory_size(other.memory_size) {};
template<typename U>
PreAllocator(const PreAllocator<U>& other) throw() : memory_ptr(other.memory_ptr), memory_size(other.memory_size) {};
template<typename U>
PreAllocator& operator = (const PreAllocator<U>& other) { return *this; }
PreAllocator<T>& operator = (const PreAllocator& other) { return *this; }
~PreAllocator() {}
pointer allocate(size_type n, const void* hint = 0) { return memory_ptr; }
void deallocate(T* ptr, size_type n) {}
size_type max_size() const { return memory_size; }
};
int main()
{
int my_arr[100] = { 0 };
std::vector<int, PreAllocator<int>> my_vec(0, PreAllocator<int>(&my_arr[0], 100));
}
The standard makes no requirement on the types of the objects that are allocated by vector using the provided allocator. The requirements are placed on the storage of the elements (the storage must be contiguous), but the implementation is free to make additional allocations of objects of other types, including the case when the allocator is used to allocate raw storage to place both internal data of the container and the elements. This point is especially relevant to node-based allocators, such as list or map, but it is also valid for vector.
Furthermore, the implementation is free to perform multiple allocation requests as a result of user requests. For example, two calls to push_back may result in two allocation requests. This means that the allocator must keep track of the previously allocated storage and perform new allocations from the unallocated storage. Otherwise, container's internal structures or previously inserted elements may get corrupted.
In this sense, the PreAllocator template, as specified in the question, indeed has multiple issues. Most importantly, it doesn't track allocated memory and always returns the pointer to the beginning of the storage from allocate. This will almost certainly cause problems, unless the user is lucky to use a specific implementation of vector that doesn't allocate anything other than the storage for its elements, and the user is very careful about the operations he invokes on the vector.
Next, the allocator does not detect storage exhaustion. This could lead to out-of-bound error conditions.
Lastly, the allocator does not ensure proper alignment of the allocated storage. The underlying buffer is only aligned to alignof(int), which may not be enough if the container allocates its internal structures that have higher alignment requirements (e.g. if the structures contain pointers, and pointers are larger than int).
The general recommendation when designing allocators is to implement them in terms of raw storage of bytes. That storage may be used to create objects of different types, sizes and alignment requirements, which may be allocated through different copies of the allocator (after rebinding to those other types). In this sense, the allocator type you pass to the container is only a handle, which may be rebound and copied by the container as it sees fit.

Is it bad practice to template array sizes when calling methods that take in arrays?

I am writing an implementation for a neural network, and I am passing in the number of nodes in each layer into the constructor. Here is my constructor:
class Network {
public:
template<size_t n>
Network(int inputNodes, int (&hiddenNodes)[n], int outputNodes);
};
I am wondering if it is bad practice to use templates to specify array size. Should I be doing something like this instead?
class Network {
public:
Network(int inputNodes, int numHiddenLayers, int* hiddenNodes, int outputNodes);
};
Templates are necessary when you want to write something that uses variable types. You don't need it when you just want to pass a value of a given type. So one argument against using a template for this is to keep things simple.
Another problem with the template approach is that you can only pass in a constant value for the size. You can't write:
size_t n;
std::cin >> n;
Network<n> network(...); // compile error
A third issue with the template approach is that the compiler will have to instantiate a specialization of the function for every possible size you are using. For small values of n, that might give some benefits, because the compiler could optimize each specialization better when it knows the exact value (for example, by unrolling loops), but for large values it will probably not be able to optimize it any better than if it didn't know the size. And having multiple specializations might mean the instruction cache in your CPU is trashed more easily, that your program's binary is larger and thus uses more disk space and memory.
So it likely is much better to pass the size as a variable, or instead of using a size and a pointer to an array, use a (reference to an) STL container, or if you can use C++20, consider using std::span.
Use std::span<int> or write your own.
struct int_span {
int* b = 0;
int* e = 0;
// iteration:
int* begin() const { return b; }
int* end() const { return e; }
// container-like access:
int& operator[](std::size_t i) const { return begin()[i]; }
std::size_t size() const { return end()-begin(); }
int* data() const { return begin(); }
// implicit constructors from various contiguous buffers:
template<std::size_t N>
int_span( int(&arr)[N] ):int_span( arr, N ) {}
template<std::size_t N>
int_span( std::array<int, N>& arr ):int_span( arr.data(), N ) {}
template<class A>
int_span( std::vector<int, A>& v ):int_span(v.data(), v.size()) {}
// From a pair of pointers, or pointer+length:
int_span( int* s, int* f ):b(s),e(f) {}
int_span( int* s, std::size_t len ):int_span(s, s+len) {}
// special member functions. Copy is enough:
int_span() = default;
// This is a view type; so assignment and copy is copying the selection,
// not the contents:
int_span(int_span const&) = default;
int_span& operator=(int_span const&) = default;
};
there we go; an int_span with represents a view into a contiguous buffer of ints of some size.
class Network {
public:
Network(int inputNodes, int_span hiddenNodes, int outputNodes);
};
From the way you write the second function argument
int (&hiddenNodes)[n]
I guess you're not an experienced C/C++ programmer. The point is that n will be ignored by the compiler and you'll lose any possibility to verify that the size of the C-style array you'll input here and the n passed as the template parameter will be equal to each other or at least coherent with each other.
So, forget about templates. Go std::vector<int>.
The only advantage of using a template (or std::array) here is that the compiler might optimize your code better than with std::vector. The chances that you'll be able to exploit it are, however, very small, and even if you succeed, the speedup most likely be hardly measureable.
The advantage of std::vector is that it is practically as fast and easy to use as std::array, but far more flexible (its size is adjustable at runtime). If you go std::array or templates and you are going to use in your program hidden layers of different sizes, soon you'll have to turn other parts of your program into templates and it is likely that rather than implementing your neural network, you'll find yourself fighting with templates. It's not worth it.
However, when you'll have a working implementation of your NN, based on std::vector, you can THEN consider its optimization, which may include std::array or templates. But I'm 99.999% sure you'll stay with std::vector.
I've never implemented a neural network, but did a lot of time-consuming simulations. The first choice is always std::vector and only if one has some special, well defined requirements for the data container does one use other containers.
Finally, keep in mind that std::array is stack-allocated, whereas std::vector is allocated on the heap. Heap is much larger and in some scenarios this is a crucial factor to consider.
EDIT
In short:
if an array size may vary freely, never pass its value as a
template parameter. Use std::vector
If it can take on 2, 3, 4, perhaps 5 sizes from a fixed set, you CAN consider std::array, but std::vector will most likely be as efficient and the code will be simpler
If the array will always be of the same size known at compile-time, and the limited size of the function stack is not an issue, use std::array.

Implications of T[n] vs T* style array in a class member

I'm implementing a super simple container for long term memory management, and the container will have inside an array.
I was wondering, what are the actual implications of those two approaches below?
template<class T, size_t C>
class Container
{
public:
T objects[C];
};
And:
template<class T>
class Container
{
public:
Container(size_t cap)
{
this->objects = new T[cap];
}
~Container()
{
delete[] this->objects;
}
T* objects;
};
Keep in mind that those are minimal examples and I'm not taking into account things like storing the capacity, the virtual size, etc.
If the size of the container is known at compile time, like in first example, you should better use std::array. For instance:
template<class T, size_t C>
class Container
{
public:
std::array<T, C> objects;
};
This has important advantages:
You can get access to its element via std::get, which automatically checks that the access is within bounds, at compile time.
You have iterators for Container::objects, so you can use all the routines of the algorithm library.
The second example has some important drawbacks:
You cannot enforce bounds-check when accessing the elements: this can potentially lead to bugs.
What happens if new in the constructor throws? You have to manage this case properly.
You need a suitable copy constructor and assignment operators.
you need a virtual destructor unless you are sure that nobody derives from the class, see here.
You can avoid all these problems by using a std::vector.
In addition to #francesco's answer:
First example
In your first example, your Container holds a C-style array. If an instance of the Container is created on the stack, the array will be on the stack as well. You might want to read heap vs stack (or similar). So, allocating on the stack can have advantages, but you have to be careful with the size you give to the array (size_t C) in order to avoid a stack overflow.
You should consider using std::array<T,C>.
Second example
Here you hold a pointer of type T which points to a C-style array which you allocate on the heap (it doesn't matter whether you allocate an instance of Container on the stack or on the heap). In this case, you don't need to know the size at compile time, which has obvious advantages in many situations. Also, you can use much greater values for size_t C.
You should consider using std::vector<T>.
Further research
For further research, read on stack vs heap allocation/performance, std::vector and std::array.

Generic vector class, make 0 size reference to generic array members;

I have a simple template vector class like this:
template <typename T, size_t N>
class Vec {
public:
T v[N];
//T const& x = v[0];
...
}
Can I make references to the array members without size cost? Becuse if I write the commented out code, it will allocate the size for the pointer, is there a workaround or a #define or some kind of magic?
No, there is no way to add a reference-type member to a class for 0 size cost. A reference is just a fancier, safer, and more convenient pointer. It still points to some specific memory location and needs to store the address of that location.
Can I make references to the array members without size cost?
Yes. References with automatic storage duration do not (always) need to require storage. Depending on the case, they may need to be stored on the stack, but will not grow the size of Vec. So, you can use a function that returns the reference:
T const& Vec::first() const { return v[0]; }
Incidentally, std::vector and other containers also provide similar functionality.

Is this a variable length array or no?

In my attempt to create a custom string class without dynamic memory, I'm using the template array length trick. Because the size is passed as a template parameter, it is known at compile time. So therefore char buffer[n] is not a variable length array. Is this correct line of thinking? Here's code:
template<typename T, size_t n>
class string_test
{
using Type = T;
// Don't count NUL byte
size_t _size = n - 1;
char buffer[n]; // <--- variable length array?
public:
string_test(const char* str)
{
strcpy(buffer, str);
}
size_t size() const
{
return _size;
}
const char* c_str() const
{
return buffer;
}
};
template<typename T, size_t n>
string_test<T, n> make_string(const T (&str)[n])
{
return string_test<T, n>(str);
}
Hopefully by this method all memory is on stack and I don't run into any issues with new, delete and so on.
Yes, your thinking is correct: buffer is not a VLA.
Hopefully by this method all memory is on stack and I don't run into any issues with new, delete and so on.
This is also correct in the sense that you don't need to manage any memory by hand.
One (potentially significant) wrinkle is that string_test<T, m> and string_test<T, n> are different types when m != n.
In general it would seem more appropriate to simply use std::vector<T>. This will lead to straightforward yet correct code with little scope for memory errors.
The code and the thinking is not wrong; and will work as is.
I would question the motive and the means to achieve it. The class is not safe in terms of bounds checking. For instance, if ctor is given a string exceeding the capacity, BANG.
Heap memory management is as efficient as it can be, and I do not foresee performance problems for 99.99% applications. If you REALLY are trying to squeeze the last CPU performance drops, perhaps using STL is not the right choice. You should limit yourself to classic C-style programming with hand-crafted optimized algorithms.
Last, I second PaulMcKenzie above that, if you want to customize memory management for any of the containers (string included), use std class with custom allocator. You can construct an allocator with automatic buffer on the stack and make it use that buffer.