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.
Related
I am using some C-library in my C++ code. The library wants me to allocate some amount of the memory and pass the pointer to the library. Unfortunately, the exact required memory size is not known in advance, so the library also requires me to provide C-callback with the following signature:
void* callback_realloc(void* ptr, size_t new_size);
where ptr is previously passed memory and new_size is required size, and the callback must return the pointer to newly allocated memory. There is no direct way to store the allocator state. Instead, I need to rely on pointer arithmetic somehow as the following:
template<class T>
struct o_s {
std::aligned_storage_t<sizeof(T), alignof(T)> data;
};
template<class Alloc>
struct o_i: private Alloc {
std::size_t allocated_size;
const Alloc& get_allocator() const { return *this; }
};
template<class T>
struct o: public o_i, public o_s<T> {
void* ptr() {
return &data;
}
// Additionally, override class operator new and operator delete...
};
void* my_callback(void* ptr, size_t new_size) {
auto meta = static_cast<o*>(reinterpret_cast<o_s<char>*>(ptr));
// access to the allocator state ...
}
Then sizeof(o_i) + initial_size memory is allocated and ptr() is passed to the C library.
At this point, I understand that I am not the first person in the world who needs this pattern. Unfortunately (and surprisingly) I have not found anything suitable for this in Boost or STL. I would like to use ready implementation to avoid possible underwater rocks.
The simplest solution is to allocate the memory using std::malloc, and use std::realloc as the callback. C API such as this are the case where using those makes sense in C++.
You don't necessarily have to use std::malloc however. You can implement a custom allocator if you want to. Using some of the allocated storage is one way of storing allocation metadata, and it's an efficient way. That's not necessary either though, since you can also store the metadata separately in a map-like structure.
I don't think you'd find an idiomatic way to do this in C++, as this is a C idiom.
Conceptually, you have two ways of going about this:
Store the metadata alongside the buffer you've allocated (as you seem to be doing). I feel using double inheritance etc. is a bit overkill here, when you could just allocate a char buffer of sizeof(size_t)+allocation_size and use the first part for your metadata.
Allocate and return to the API a raw buffer as needed, and use a separate static data structure to manage this with a map of ptr->allocation size. I suppose this is what malloc/realloc is doing behind the scenes anyway.
Both are valid, and the one you chose depends on the specific details of your application. When dealing with memory it's ok to actually deal with memory, be it pointer arithmetic or what not.
The important thing is probably to keep the ugly bit to a single location, and provide a C++ style API to this on the C++ side of things, so that the client code isn't exposed to implementation details.
For my project I need to store pointers to objects of type ComplicatedClass in an array. This array is stored in a class Storage along with other information I have omitted here.
Here's what I would like to do (which obviously doesn't work, but hopefully explains what I'm trying to achieve):
class ComplicatedClass
{
...
}
class Storage
{
public:
Storage(const size_t& numberOfObjects, const std::array<ComplicatedClass *, numberOfObjects>& objectArray)
: size(numberOfObjects),
objectArray(objectArray)
{}
...
public:
size_t size;
std::array<ComplicatedClass *, size> objectArray;
...
}
int main()
{
ComplicatedClass * object1 = new ComplicatedClass(...);
ComplicatedClass * object2 = new ComplicatedClass(...);
Storage myStorage(2, {object1, object2});
...
return 0;
}
What I am considering is:
Using std::vector instead of std::array. I would like to avoid this because there are parts of my program that are not allowed to allocate memory on the free-store. As far as I know, std::vector would have to do that. As a plus I would be able to ditch size.
Changing Storage to a class template. I would like to avoid this because then I have templates all over my code. This is not terrible but it would make classes that use Storage much less readable, because they would also have to have templated functions.
Are there any other options that I am missing?
How can I pass and store an array of variable size containing pointers to objects?
By creating the objects dynamically. Most convenient solution is to use std::vector.
size_t size;
std::array<ComplicatedClass *, size> objectArray;
This cannot work. Template arguments must be compile time constant. Non-static member variables are not compile time constant.
I would like to avoid this because there are parts of my program that are not allowed to allocate memory on the free-store. As far as I know, std::vector would have to do that.
std::vector would not necessarily require the use of free-store. Like all standard containers (besides std::array), std::vector accepts an allocator. If you implement a custom allocator that doesn't use free-store, then your requirement can be satisfied.
Alternatively, even if you do use the default allocator, you could write your program in such way that elements are inserted into the vector only in parts of your program that are allowed to allocate from the free-store.
I thought C++ had "free-store" instead of heap, does it not?
Those are just different words for the same thing. "Free store" is the term used in C++. It's often informally called "heap memory" since "heap" is a data structure that is sometimes used to implement it.
Beginning with C++11 std::vector has the data() method to access the underlying array the vector is using for storage.
And in most cases a std::vector can be used similar to an array allowing you to take advantage of the size adjusting container qualities of std::vector when you need them or using it as an array when you need that. See https://stackoverflow.com/a/261607/1466970
Finally, you are aware that you can use vectors in place of arrays,
right? Even when a function expects c-style arrays you can use
vectors:
vector<char> v(50); // Ensure there's enough space
strcpy(&v[0], "prefer vectors to c arrays");
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.
There is an example class in a book I am reading used throughout to explain concepts:
class Gameboard{
public:
Gameboard(int inWidth, int inHeight);
Gameboard(const Spreadsheet& src);
Gameboard& operator=(const Spreadsheet& rhs);
private:
GamePiece** mCells;
size_t width;
size_t height;
};
they then introduce templates and introduce an updated class:
template<typename T>
class Grid{
public:
Grid<T>(int inWidth, int inHeight);
Grid<T>(const T& src);
Grid<T>& operator=(const T& rhs);
private:
T** mCells;
size_t width;
size_t height;
};
finally they introduce non-type template parameters and say you can now do this:
template<typename T, size_t WIDTH, size_t HEIGHT>
class Grid{
public:
Grid<T>();
Grid<T>(const T& src);
Grid<T>& operator=(const T& rhs);
private:
T mCells[WIDTH][HEIGHT];
};
From the book:
In the Grid template class, you could use non-type template parameters to specify the height and width of the grid instead of specifying them in the constructor. The principle advantage to specifying non-type parameters in the template list instead of the constructor is that the values are known before the code is compiled. Recall that the compiler generates code for templatized methods by substituting in the template parameters before compiling. Thus you can use a normal two-dimensional array in your implementation instead of dynamically allocating it.
I don't get all the excitement with this approach regarding the dynamic memory allocation. Firstly does this mean the multidimensional array would be on the stack (because they seem to suggest it wouldnt be dynamically allocated)? I don't understand why you wouldn't want to dynamically allocate the memory on the heap?
Secondly, is there some C++ rule (which I am forgetting) which prohibits declaring a multidimensional array on the stack, hence the excitement with this approach?
I am trying to understand what is the advantage of using non-type template parameters in their example.
Firstly does this mean the multidimensional array would be on the stack (because they seem to suggest it wouldnt be dynamically allocated)?
As you can see, your array is directly a member, it's not indirected by a pointer.
T mCells[WIDTH][HEIGHT];
But it wouldn't be correct to say that it's on the stack or the heap since in fact the array is a part of your object and where it is depends on where and how your object is allocated. If the object is allocated on the heap, its array subobject will be too, if the whole object is on the stack, so will the array be.
I don't understand why you wouldn't want to dynamically allocate the memory on the heap?
You could. But having a new'd array is both slower and more error-prone (i.e. it should be deleted etc).
Secondly, is there some C++ rule (which I am forgetting) which prohibits declaring a multidimensional array on the stack, hence the excitement with this approach?
No. But the size of any array must be a compile-time constant. Non-type template parameters are exactly that - compile time constants. Had they been simply function parameters of an int type, the compiler would not know their value and it would be illegal to create an array (multidimensional or otherwise) on the stack.
"Firstly does this mean the multidimensional array would be on the stack"
If Gameboard is located on the stack (ie it's a local variable) then yes, the array is also on the stack.
"I don't understand why you wouldn't want to dynamically allocate the memory on the heap?"
For speed. In this case, as Gameboard will probably stick around for a long time, it's not necessary. Actually, std::vector<std::vector<GamePiece>> mCells would be better than manual arrays.
"is there some C++ rule (which I am forgetting) which prohibits declaring a multidimensional array on the stack"
No, it's permissible. But like normal arrays, the dimensions must be known at compile-time.
"I am trying to understand what is the advantage of using non-type template parameters in their example."
It is a contrived example.
Consider you want to create an arbitrary-precision integer class. The higher the precision, the more space is required. This class may be created and destroyed frequently, just like a regular integer variable. If the data is allocated dynamically, this is slower. By specifying the required precision with a template parameter, a regular array of the required size can be created as a member of the class. Whenever the class is placed on the stack, so will the array, and this will be faster.
Without referring to the particular samples and sources you give:
I don't get all the excitement with this approach regarding the dynamic memory allocation
Because no more dynamic allocation is needed for such at all, even if you're creating instances on the stack.
I don't understand why you wouldn't want to dynamically allocate the memory on the heap?
I'm often working on small embedded systems, where I sometimes don't even have the possibility for dynamic memory management (or I don't want to bear the overhead). For such systems, and where you very well know in advance, which sizes you can/want actually have, it's a pretty good configuration abstraction you want to use for target specific platform implementations.
Also besides the above reasons, dynamic allocation introduces a performance hit at runtime that is unwanted for performance critical applications (e.g. game framework rendering engines).
In short and general:
If you have anything that can be certainly configured at compile time, prefer this over configuring at run time.
Recently, I have seen some Matrix and 1D array classes implemented in C++ where each individual element is wrapped as a class (e.g. Element class). Normally, we like to have containers like Matrix to contain actual elements (e.g. int) consecutive in a memory. Using a custom class for individual elements can give you some flexibility but what are the possible drawbacks?
To make it short, see the pseudo-code:
// 1st approach: Elements stored in their type.
template <class T>
class Matrix
{
T *m_data;
//..
};
// 2nd approach: Elements wrapped into a class
template<class T>
class Matrix
{
std::set<Element<T> > m_data; // or using std::vector<Element<T> > m_data
//..
}; // Element is a class representing single element of type T
what could be the implications of this second approach, specially if we need to use Matrix for large amount of data? Also, if we need to use this type with GPU programming(transfering to device memory back and forth)?
One drawback is a memory cost for each Element, which could be a performance concern in large collections. It will cost you at least a byte, probably more for the padding. The "sizeof" operator should tell you the cost.
If the class has no virtual functions, they will probably* be placed in contiguous memory with something like new Element[20] or std::vector<Element> v(20). As noted above, std::set and most other STL containers are not necessarily contiguous.
*I say "probably" because depending on the size of the actual type, the compiler might insert some padding, which you can probably control with #pragmas as needed.