for an embedded system we need a custom vector class, where the capacity is set during compile-time through a template parameter.
Until now we had an array of objects as a member variable.
template<class T, size_t SIZE>
class Vector {
...
T data[SIZE];
}
The problem here of course is that if T isn't a POD, the default constructors of T are called. Is there any way to let data be uninitialized until a corresponding push() call (with placement new inside)? Just using
uint8_t data[SIZE * sizeof(T)];
possibly breaks the alignment of T. We absolutely cannot use dynamic memory, the total container size always needs to be known at compile-time. We also cannot use C++'s alignas specifier since the compiler does not support C++11 yet :(
First I would check if the compiler has support for alignment, ie gcc has __attribute__(aligned(x)), there is likely something similar.
Then if you absolutely have to have aligned uninitialized data without such support, you will have to waste some space
// Align must be power of 2
template<size_t Len, size_t Align>
class aligned_memory
{
public:
aligned_memory()
: data((void*)(((std::uintptr_t)mem + Align - 1) & -Align)) {}
void* get() const {return data;}
private:
char mem[Len + Align - 1];
void* data;
};
And you'd use placement new with it
template<typename T, size_t N>
class Array
{
public:
Array() : sz(0) {}
void push_back(const T& t)
{
new ((T*)data.get() + sz++) T(t);
}
private:
aligned_memory<N * sizeof(T), /* alignment */> data;
size_t sz;
};
Live
The alignment of T can be found with C++11 alignof, check your compiler to see if it supports anything that can be used to find out its alignment. You can also just take a guess from printed pointer values and hope that's enough.
Another way is to use std::vector<> with a custom allocator that allocates on the stack.
This way you would create an empty vector, reserve the required space, which should be equal to the space your allocator allocates for you on the stack, and then populate the vector using vector<>::emplace_back. Your element type can be non-copyable but must be movable in this case.
E.g.:
#include <vector>
struct X {
X(int, int);
// Non-copyable.
X(X const&) = delete;
X& operator=(X const&) = delete;
// But movable.
X(X&&);
X& operator=(X&&);
};
template<class T, std::size_t N>
struct MyStackAllocator; // Implement me.
int main() {
std::vector<X, MyStackAllocator<X, 10>> v;
v.reserve(10);
v.emplace_back(1, 2);
v.emplace_back(3, 4);
}
Information about how to implement an allocator is widely available, for example, search YouTube for "c++ allocator".
You are going to have to use placement new along with a union trick to get the alignment properly set.
// use `std::max_align_t` and `std::aligned_storage` when you have it
// since don't have access to alignof(), use the presumably max
// alignment value
using MaxAlign = long;
template <typename T, int size>
class UninitializedArray {
union Node {
char data[sizeof(T)];
MaxAlign alignment;
};
Node aligned_data[size];
bool initialized;
public:
UninitializedArray() : initialized(false) {}
void initialize() {
for (int i = 0; i < static_cast<int>(size); ++i) {
new (&this->aligned_data[i].data) T();
}
this->initialized = true;
}
~UninitializedArray() {
if (this->initialized) {
for (int i = 0; i < static_cast<int>(size); ++i) {
T* ptr = reinterpret_cast<T*>(&this->aligned_data[i].data);
ptr->~T();
}
}
}
T& operator[](int index) {
if (!this->initialized) {
this->initialize();
}
T* ptr = reinterpret_cast<T*>(&this->aligned_data[i].data);
return *ptr;
}
};
And then use it like this
UninitializedArray<Something, 5> arr;
arr[0].do_something();
If you ever get C++17 working, then you can use std::array and std::optional to make this easy
std::optional<std::array<T, N>> optional_array;
// construct the optional, this will construct all your elements
optional_array.emplace();
// then use the value in the optional by "treating" the optional like
// a pointer
optional_array->at(0); // returns the 0th object
Related
I am working on custom allocators. So far, I have tried to work on simple containers: std::list, std::vector, std::basic_string, etc...
My custom allocator is a static buffer allocator, its implementation is straightforward:
#include <memory>
template <typename T>
class StaticBufferAlloc : std::allocator<T>
{
private:
T *memory_ptr;
std::size_t memory_size;
public:
typedef std::size_t size_type;
typedef T *pointer;
typedef T value_type;
StaticBufferAlloc(T *memory_ptr, size_type memory_size) : memory_ptr(memory_ptr), memory_size(memory_size) {}
StaticBufferAlloc(const StaticBufferAlloc &other) throw() : memory_ptr(other.memory_ptr), memory_size(other.memory_size){};
pointer allocate(size_type n, const void *hint = 0) { return memory_ptr; } // when allocate return the buffer
void deallocate(T *ptr, size_type n) {} // empty cause the deallocation is buffer creator's responsability
size_type max_size() const { return memory_size; }
};
I am using it in this fashion:
using inner = std::vector<int, StaticBufferAlloc<int>>;
int buffer[201];
auto alloc1 = StaticBufferAlloc<int>(&buffer[100], 50);
inner v1(0, alloc1);
assert(v1.size() == 0);
const int N = 10;
// insert 10 integers
for (size_t i = 0; i < N; i++) {
v1.push_back(i);
}
assert(v1.size() == N);
All good so far, when I grow N past the max buffer size it throws and that's expected.
Now, I am trying to work with nested containers. In short, am trying to have a vector of the vector (matrix), where the parent vector and all its underlying elements (that are vectors i.e. containers) share the same static buffer for allocation. It looks like scoped_allocator can be a solution for my problem.
using inner = std::vector<int, StaticBufferAlloc<int>>;
using outer = std::vector<inner, std::scoped_allocator_adaptor<StaticBufferAlloc<inner>>>;
int buffer[201];
auto alloc1 = StaticBufferAlloc<int>(&buffer[100], 50);
auto alloc2 = StaticBufferAlloc<int>(&buffer[150], 50);
inner v1(0, alloc1);
inner v2(0, alloc2);
assert(v1.size() == 0);
assert(v2.size() == 0);
const int N = 10;
// insert 10 integers
for (size_t i = 0; i < N; i++)
{
v1.push_back(i);
v2.push_back(i);
}
assert(v1.size() == N);
assert(v2.size() == N);
outer v // <- how to construct this vector with the outer buffer?
v.push_back(v1);
v.push_back(v2);
...
My question is how to initialize the outer vector on its constructor call with its static buffer?
Creating a scoped allocator in C++11/C++14 was a little bit challenging. So I opted for a very modern solution introduced in C++17. Instead of implementing an allocator, I used polymorphic_allocator. Polymorphic allocators are scoped allocators, standard containers will automatically pass the allocators to sub-objects.
Basically, the idea was to use a polymorphic allocator and inject it with monotonic_buffer_resource. The monotonic_buffer_resource can be initialized with a memory resource.
Writing a custom memory resource was very simple:
class custom_resource : public std::pmr::memory_resource
{
public:
explicit custom_resource(std::pmr::memory_resource *up = std::pmr::get_default_resource())
: _upstream{up}
{
}
void *do_allocate(size_t bytes, size_t alignment) override
{
return _upstream; //do nothing, don't grow just return ptr
}
void do_deallocate(void *ptr, size_t bytes, size_t alignment) override
{
//do nothing, don't deallocate
}
bool do_is_equal(const std::pmr::memory_resource &other) const noexcept override
{
return this == &other;
}
private:
std::pmr::memory_resource *_upstream;
};
Using it is even simpler:
std::byte buffer[512];
custom_resource resource;
std::pmr::monotonic_buffer_resource pool{std::data(buffer), std::size(buffer), &resource};
std::pmr::vector<std::pmr::vector<int>> outer(&pool)
It is important to note that std::pmr::vector<T> is just std::vector<T, polymorphic_allocator>.
Useful resources:
CppCon 2017: Pablo Halpern “Allocators: The Good Parts”
C++ Weekly - Ep 222 - 3.5x Faster Standard Containers With PMR
Purpose of scoped allocator
std::pmr is cool but it requires modern versions of gcc to run (9+). Fortunately, Reddit is full of kind strangers. A C++14 solution can be found here.
I believe this is a simple question with probably a not simple answer.
Here is the code:
template<typename T>
T* copy(T* original, int size) {
T* result = new T[size];
// At this point the default constructor of all new T objects have been called.
for(int i = 0; i < size; ++i) {
// This will call the assignment operator= on all new T objects
result[i] = original[i];
}
return result;
}
Question:
Is there a way to initialize the newly allocated memory using the copy constructor of T instead of using default constructor followed by assignment operator?
The purpose is to copy each element to its analogous element in the new array using the copy constructor of T.
I imagine there could be a way to do that by allocating memory using malloc, then calling the copy constructor for each element but I don't know how.
Here is an example solution from my imagination. If this is correct or this is the best we can get, tell me. Or propose a better solution:
template<typename T>
T* copy(T* original, int size) {
T* result = malloc(sizeof(T)*size);
// At this point the default constructor of all new T objects have been called.
for(int i = 0; i < size; ++i) {
T t(original[i]);
memcpy(result+i*sizeof(T), &t, sizeof(T));
}
return result;
}
Note: Raw pointers are being used for simplicity.
Note 2: I don't need a vector. This pattern will be used to copy the underlying data structure of more complicated objects.
You will have to allocate memory by any other means, but keep in mind that size * sizeof(T) can overflow. std::allocator takes care of this.
Use std::uninitialized_copy/std::uninitialized_copy_n to perform the copy:
template<typename T>
T* copy(T* original, int size) {
std::allocator<T> alloc;
T* result = alloc.allocate(size);
try {
std::uninitialized_copy_n(original, size, result);
} catch (...) {
alloc.deallocate(result, size);
throw;
}
return result;
}
Later you can use std::destroy/std::destroy_n to destroy them and deallocate memory:
template<typename T>
void destroy(T* ptr, int size)
{
std::destroy_n(ptr, size);
std::allocator<T>().deallocate(ptr, size);
}
This should work unless you need to be able to delete them with operator delete[] - in which case there is no solution for this.
If you are implementing a custom container, you can use template allocator like standard containers do:
template<typename T, typename Allocator = std::allocator<T>>
struct container
{
[[no_unique_address]] Allocator allocator;
...
};
For the new operator I don't think so.
But yes there is. It's called std::vector:
template<typename T>
std::vector<T> copy(T* original, int size) {
return std::vector<T>{original, original + size};
}
Because you don't follow RAII and use owning raw pointers your code is not memory leak free, so don't do that! Use C++ properly.
well i cant find how do this, basically its a variable union with params, basic idea, (writed as function)
Ex1
union Some (int le)
{
int i[le];
float f[le];
};
Ex2
union Some
{
int le;
int i[le];
float f[le];
};
obs this don't works D:
maybe a way to use an internal variable to set the lenght but don't works too.
Thx.
No, this is not possible: le would need to be known at compile-time.
One solution would be to use a templated union:
template <int N> union Some
{
int i[N];
float f[N];
};
N, of course, is compile-time evaluable.
Another solution is the arguably more succinct
typedef std::vector<std::pair<int, float>> Some;
or a similar solution based on std::array.
Depending on your use case you could try to simulate a union.
struct Some
{
//Order is important
private:
char* pData;
public:
int* const i;
float* const f;
public:
Some(size_t len)
:pData(new char[sizeof(int) < sizeof(float) ? sizeof(float) : sizeof(int)])
,i ((int*)pData)
,f ((float*)pData)
{
}
~Some()
{
delete[] pData;
}
Some(const Some&) = delete;
Some& operator=(const Some&) = delete;
};
Alternative solution using templates, unique_ptr and explicit casts:
//max_size_of<>: a recursive template struct to evaluate the
// maximum value of the sizeof function of all types passed as
// parameter
//The recursion is done by using the "value" of another
// specialization of max_size_of<> with less parameter types
template <typename T, typename...Args>
struct max_size_of
{
static const std::size_t value = std::max(sizeof(T), max_size_of<Args...>::value);
};
//Specialication for max_size_of<> as recursion stop
template <typename T>
struct max_size_of<T>
{
static const std::size_t value = sizeof(T);
};
//dataptr_auto_cast<>: a recursive template struct that
// introduces a virtual function "char* const data_ptr()"
// and an additional explicit cast operator for a pointer
// of the first type. Due to the recursion a cast operator
// for every type passed to the struct is created.
//Attention: types are not allowed to be duplicate
//The recursion is done by inheriting from of another
// specialization of dataptr_auto_cast<> with less parameter types
template <typename T, typename...Args>
struct dataptr_auto_cast : public dataptr_auto_cast<Args...>
{
virtual char* const data_ptr() const = 0; //This is needed by the cast operator
explicit operator T* const() const { return (T*)data_ptr(); } //make it explicit to avoid unwanted side effects (manual cast needed)
};
//Specialization of dataptr_auto_cast<> as recursion stop
template <typename T>
struct dataptr_auto_cast<T>
{
virtual char* const data_ptr() const = 0;
explicit operator T* const() const { return (T*)data_ptr(); }
};
//union_array<>: inherits from dataptr_auto_cast<> with the same
// template parameters. Also has a static const member "blockSize"
// that indicates the size of the largest datatype passed as parameter
// "blockSize" is used to determine the space needed to store "size"
// elements.
template <typename...Args>
struct union_array : public dataptr_auto_cast<Args...>
{
static const size_t blockSize = max_size_of<Args...>::value;
private:
std::unique_ptr<char[]> m_pData; //std::unique_ptr automatically deletes the memory it points to on destruction
size_t m_size; //The size/no. of elements
public:
//Create a new array to store "size" elements
union_array(size_t size)
:m_pData(new char[size*blockSize])
,m_size(size)
{
}
//Copy constructor
union_array(const union_array<Args...>& other)
:m_pData(new char[other.m_size*blockSize])
,m_size(other.m_size)
{
memcpy(m_pData.get(), other.m_pData.get(), other.m_size);
}
//Move constructor
union_array(union_array<Args...>&& other)
:m_pData(std::move(other.m_pData))
,m_size(std::move(other.m_size))
{
}
union_array& operator=(const union_array<Args...>& other)
{
m_pData = new char[other.m_size*blockSize];
m_size = other.m_size;
memcpy(m_pData.get(), other.m_pData.get(), other.m_size);
}
union_array& operator=(union_array<Args...>&& other)
{
m_pData = std::move(other.m_pData);
m_size = std::move(other.m_size);
}
~union_array() = default;
size_t size() const
{
return m_size;
}
//Implementation of dataptr_auto_cast<>::data_ptr
virtual char* const data_ptr() const override
{
return m_pData.get();
}
};
int main()
{
auto a = union_array<int, char, float, double>(5); //Create a new union_array object with enough space to store either 5 int, 5 char, 5 float or 5 double values.
((int*)a)[3] = 3; //Use as int array
auto b = a; //copy
((int*)b)[3] = 1; //Change a value
auto c = std::move(a);// move a to c, a is invalid beyond this point
// std::cout << ((int*)a)[3] << std::endl; //This will crash as a is invalid due to the move
std::cout << ((int*)b)[3] << std::endl; //prints "1"
std::cout << ((int*)c)[3] << std::endl; //prints "3"
}
Explanation
template <typename T, typename...Args>
struct max_size_of
{
static const std::size_t value = std::max(sizeof(T), max_size_of<Args...>::value);
};
template <typename T>
struct max_size_of<T>
{
static const std::size_t value = sizeof(T);
};
max_size_of<> is used to get the largest sizeof() value of all types passed as template paremeters.
Let's have a look at the simple case first.
- max_size_of<char>::value: value will be set to sizeof(char).
- max_size_of<int>::value: value will be set to sizeof(int).
- and so on
If you put in more than one type it will evaluate to the maximum of the sizeof of these types.
For 2 types this would look like this: max_size_of<char, int>::value: value will be set to std::max(sizeof(char), max_size_of<int>::value).
As described above max_size_of<int>::value is the same as sizeof(int), so max_size_of<char, int>::value is the same as std::max(sizeof(char), sizeof(int)) which is the same as sizeof(int).
template <typename T, typename...Args>
struct dataptr_auto_cast : public dataptr_auto_cast<Args...>
{
virtual char* const data_ptr() const = 0;
explicit operator T* const() const { return (T*)data_ptr(); }
};
template <typename T>
struct dataptr_auto_cast<T>
{
virtual char* const data_ptr() const = 0;
explicit operator T* const() const { return (T*)data_ptr(); }
};
dataptr_auto_cast<> is what we use as a simple abstract base class.
It forces us to implement a function char* const data_ptr() const in the final class (which will be union_array).
Let's just assume that the class is not abstract and use the simple version dataptr_auto_cast<T>:
The class implements a operator function that returns a pointer of the type of the passed template parameter.
dataptr_auto_cast<int> has a function explicit operator int* const() const;
The function provides access to data provided by the derived class through the data_ptr()function and casts it to type T* const.
The const is so that the pointer isn't altered accidentially and the explicit keyword is used to avoid unwanted implicit casts.
As you can see there are 2 versions of dataptr_auto_cast<>. One with 1 template paremeter (which we just looked at) and one with multiple template paremeters.
The definition is quite similar with the exception that the multiple parameters one inherits dataptr_auto_cast with one (the first) template parameter less.
So dataptr_auto_cast<int, char> has a function explicit operator int* const() const; and inherits dataptr_auto_cast<char> which has a function explicit operator char* const() const;.
As you can see there is one cast operator function implemented with each type you pass.
There is only one exception and that is passing the same template parameter twice.
This would lead in the same operator function being defined twice within the same class which doesn't work.
For this use case, using this as a base class for the union_array, this shouldn't matter.
Now that these two are clear let's look at the actual code for union_array:
template <typename...Args>
struct union_array : public dataptr_auto_cast<Args...>
{
static const size_t blockSize = max_size_of<Args...>::value;
private:
std::unique_ptr<char[]> m_pData;
size_t m_size;
public:
//Create a new array to store "size" elements
union_array(size_t size)
:m_pData(new char[size*blockSize])
,m_size(size)
{
}
//Copy constructor
union_array(const union_array<Args...>& other)
:m_pData(new char[other.m_size*blockSize])
,m_size(other.m_size)
{
memcpy(m_pData.get(), other.m_pData.get(), other.m_size);
}
//Move constructor
union_array(union_array<Args...>&& other)
:m_pData(std::move(other.m_pData))
,m_size(std::move(other.m_size))
{
}
union_array& operator=(const union_array<Args...>& other)
{
m_pData = new char[other.m_size*blockSize];
m_size = other.m_size;
memcpy(m_pData.get(), other.m_pData.get(), other.m_size);
}
union_array& operator=(union_array<Args...>&& other)
{
m_pData = std::move(other.m_pData);
m_size = std::move(other.m_size);
}
~union_array() = default;
size_t size() const
{
return m_size;
}
virtual char* const data_ptr() const override
{
return m_pData.get();
}
};
As you can see union_array<> inherits from dataptr_auto_cast<> using the same template arguments.
So this gives us a cast operator for every type passed as template paremeter to union_array<>.
Also at the end of union_array<> you can see that the char* const data_ptr() const function is implemented (the abstract function from dataptr_auto_cast<>).
The next interesting thing to see is static const size_t blockSize which is initilialized with the maximum sizeof value of the template paremeters to union_array<>.
To get this value the max_size_of is used as described above.
The class uses std::unique_ptr<char[]> as data storage, as std::unique_ptr automatically will delete the space for us, once the class is destroyed.
Also std::unique_ptr is capable of move semantics, which is used in the move assign operator function and the move constructor.
A "normal" copy assign operator function and a copy constructor are also included and copy the memory accordingly.
The class has a constructor union_array(size_t size) which takes the number of elements the union_array should be able to hold.
Multiplying this value with blockSize gives us the space needed to store exactly size elements of the largest template type.
Last but not least there is an access method to ask for the size() if needed.
C++ requires that the size of a type be known at compile time.
The size of a block of data need not be known, but all types have known sizes.
There are three ways around it.
I'll ignore the union part for now. Imagine if you wanted:
struct some (int how_many) {
int data[how_many];
};
as the union part adds complexity which can be dealt with separately.
First, instead of storing the data as part of the type, you can store pointers/references/etc to the data.
struct some {
std::vector<int> data;
explicit some( size_t how_many ):data(how_many) {};
some( some&& ) = default;
some& operator=( some&& ) = default;
some( some const& ) = default;
some& operator=( some const& ) = default;
some() = default;
~some() = default;
};
here we store the data in a std::vector -- a dynamic array. We default copy/move/construct/destruct operations (explicitly -- because it makes it clearer), and the right thing happens.
Instead of a vector we can use a unique_ptr:
struct some {
std::unique_ptr<int[]> data;
explicit some( size_t how_many ):data(new int[how_many]) {};
some( some&& ) = default;
some& operator=( some&& ) = default;
some() = default;
~some() = default;
};
this blocks copying of the structure, but the structure goes from being size of 3 pointers to being size of 1 in a typical std implementation. We lose the ability to easily resize after the fact, and copy without writing the code ourselves.
The next approach is to template it.
template<std::size_t N>
struct some {
int data[N];
};
this, however, requires that the size of the structure be known at compile-time, and some<2> and some<3> are 'unrelated types' (barring template pattern matching). So it has downsides.
A final approach is C-like. Here we rely on the fact that data can be variable in size, even if types are not.
struct some {
int data[1]; // or `0` in some compilers as an extension
};
some* make_some( std::size_t n ) {
Assert(n >= 1); // unless we did `data[0]` above
char* buff = new some[(n-1)*sizeof(int) + sizeof(some)]; // note: alignment issues on some platforms?
return new(buff) some(); // placement new
};
where we allocate a buffer for some of variable size. Access to the buffer via data[13] is practically legal, and probably actually so as well.
This technique is used in C to create structures of variable size.
For the union part, you'll want to create a buffer of char with the right size std::max(sizeof(float), sizeof(int))*N, and expose functions:
char* data(); // returns a pointer to the start of the buffer
int* i() { return reinterpret_cast<int*>(data()); }
float* f() { return reinterpret_cast<float*>(data()); }
you may also need to properly initialize the data as the proper type; in theory, a char buffer of '\0's may not correspond to defined float values or ints that are zero.
I would like to suggest a different approach: Instead of tying the number of elements to the union, tie it outside:
union Some
{
int i;
float f;
};
Some *get_Some(int le) { return new Some[le]; }
Don't forget to delete[] the return value of get_Some... Or use smart pointers:
std::unique_ptr<Some[]> get_Some(int le)
{ return std::make_unique<Some[]>(le); }
You can even create a Some_Manager:
struct Some_Manager
{
union Some
{
int i;
float f;
};
Some_Manager(int le) :
m_le{le},
m_some{std::make_unique<Some[]>(le)}
{}
// ... getters and setters...
int count() const { return m_le; }
Some &operator[](int le) { return m_some[le]; }
private:
int m_le{};
std::unique_ptr<Some[]> m_some;
};
Take a look at the Live example.
It's not possible to declare a structure with dynamic sizes as you are trying to do, the size must be specified at run time or you will have to use higher-level abstractions to manage a dynamic pool of memory at run time.
Also, in your second example, you include le in the union. If what you were trying to do were possible, it would cause le to overlap with the first value of i and f.
As was mentioned before, you could do this with templating if the size is known at compile time:
#include <cstdlib>
template<size_t Sz>
union U {
int i[Sz];
float f[Sz];
};
int main() {
U<30> u;
u.i[0] = 0;
u.f[1] = 1.0;
}
http://ideone.com/gG9egD
If you want dynamic size, you're beginning to reach the realm where it would be better to use something like std::vector.
#include <vector>
#include <iostream>
union U {
int i;
float f;
};
int main() {
std::vector<U> vec;
vec.resize(32);
vec[0].i = 0;
vec[1].f = 42.0;
// But there is no way to tell whether a given element is
// supposed to be an int or a float:
// vec[1] was populated via the 'f' option of the union:
std::cout << "vec[1].i = " << vec[1].i << '\n';
}
http://ideone.com/gjTCuZ
OK, so I recently learned that (a) std::vector uses contiguous memory by definition/standard, and thus (b) &(v[0]) is the address of that contiguous block of memory, which you can read/write to as an old-skool C-array. Like...
void printem(size_t n, int* iary)
{ for (size_t i=0; i<n; ++i) std::cout << iary[i] << std::endl; }
void doublem(size_t n, int* iary)
{ for (size_t i=0; i<n; ++i) iary[i] *= 2; }
std::vector<int> v;
for (size_t i=0; i<100; ++i) v.push_back(i);
int* iptr = &(v[0]);
doublem(v.size(), iptr);
printem(v.size(), iptr);
OK, so that's cool, but I want to go in the other direction. I have lots and lots of existing code like
double computeSomething(const std::vector<SomeClass>& v) { ... }
If I have a C-array of objects, I can use such code like this:
SomeClass cary[100]; // 100*sizeof(SomeClass)
// populate this however
std::vector<SomeClass> v;
for (size_t i=0; i<100; ++i) v.push_back(cary[i]);
// now v is also using 100*sizeof(SomeClass)
double x = computeSomething(v);
I would like to do that (a) without the extra space and (b) without the extra time of inserting a redundant copy of all that data into the vector. Note that "just change your stupid computeSomething, idiot" is not sufficient, because there are thousands of such functions/methods that exhibit this pattern that are not under my control and, even if they were are too many to go and change all of them.
Note also that because I am only interested in const std::vector& usage, there is no worry that my original memory will ever need to be resized, or even modified. I would want something like a const std::vector constructor, but I don't know if the language even allows special constructors for const instances of a class, like:
namespace std { template <typename T> class vector {
vector() { ... }
vector(size_t n) { ... }
vector(size_t n, const T& t) { ... }
const vector(size_t n, T*) { ... } // can this be done?
...
If that is not possible, how about a container derived off of std::vector called std::const_vector, which (a) could construct from a pointer to a c-array and a size, and (b) purposefully did not implement non-const methods (push_back, resize, etc.), so then even if the object with a typename of const_vector is not actually a const object, the interface which only offers const methods makes it practically const (and any erroneous attempts to modify would be caught at compile time)?
UPDATE: A little messing around shows that this "solves" my problem wrt Windows-implementation of std::vector:
template <typename T>
class vector_tweaker : public std::vector<T> {
public:
vector_tweaker(size_t n, T* t) {
_saveMyfirst = _Myfirst;
_saveMylast = _Mylast;
_saveMyend = _Myend;
_Myfirst = t;
_Mylast = t + n;
_Myend = t + n;
}
~vector_tweaker() {
_Myfirst = _saveMyfirst;
_Mylast = _saveMylast;
_Myend = _saveMyend; // and proceed to std::vector destructor
}
private:
T* _saveMyfirst;
T* _saveMylast;
T* _saveMyend;
};
But of course that "solution" is hideous because (a) it offers no protection against the base class deleting the original memory by doing a resize() or push_back() (except for a careful user that only constructs const vector_tweaker()) -- and (b) it is specific to a particular implementation of std::vector, and would have to be reimplemented for others -- if indeed other platforms only declare their std::vector member data as protected: as microsoft did (seems a Bad Idea).
You can try reference-logic storing introduced in C++11 with std::reference_wrapper<>:
SomeClass cary[100];
// ...
std::vector<std::reference_wrapper<SomeClass>> cv;
cv.push_back(cary[i]); // no object copying is done, reference wrapper is stored
Or without C11, you can create a specialization of such template class for bytes - char. Then for the constructor from char* C-array you can use ::memcpy: which unfortunately will then use twice as much memory.
::memcpy(&v[0], c_arr, n);
Something like this:
template <typename T> class MyVector : public std::vector<T> {
};
template <> class MyVector<char> : public std::vector<char> {
public:
MyVector<char>(char* carr, size_t n) : std::vector<char>(n) {
::memcpy(&operator[](0), carr, n);
}
};
What I would recommend - replace all C-arrays to vectors where possible, then no extra copying will be needed.
I'm trying to learn more about templates and have come across a problem I can't seem to solve. At the moment the class below works fine.
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
template <class T, int s>
class myArray{
public:
T* data;
inline T& operator[](const int i){return data[i];}
myArray(){
data=new T[s];
}
myArray(const myArray& other){
data=new T[s];
copy(other.data,other.data+s,data);
}
myArray& operator=(const myArray& other){
data=new T[s];
copy(other.data,other.data+s,data);
return *this;
}
~myArray(){delete [] data;}
};
If I use it:
myArray<myArray<myArray<int,10>,20>,30> a;
a is now 30x20x10 array that I can access with the normal array brackets e.g. a[5][5][5]. I wish to add a feature so that I could write:
myArray<myArray<myArray<int,10>,20>,30> a(10);
and initialise all of the entries to 10 for example. I can't work out how to do this. As I understand, each layer of myArray is constructed using the default constructor. If I changed this to something like:
myArray(int n=0){
data=new T[s];
fill(data,data+s,n); //T might not be of type int so this could fail.
}
I think this should fail when data is not of type int (i.e. on any array on dimensions > 1), however it doesn't. It works when the array is square, but if not then some of the entries aren't set to 10. Does anyone have an idea how the standard vectors class achieves this? Any help would be amazing. Thanks!
Well, try something like this:
myArray()
: data(new T[s]()) // value-initialization!
{
}
myArray(T const & val)
: data(new T[s]) // default-initialization suffices
{
std::fill(data, data + s, val);
}
If you're into variadic templates, you might cook up something even more grotesque involving variadically filled initializer lists, but I think we've done enough learning for one week.
Note the fundamental flaw in using new: Either version requires that your class T can be instantiated in some "default" state, and that it be assignable, even though we never require the default state in the second version. That's why "real" libraries separate memory allocation and object construction, and you never see a new expression unless its the placement version.
std::vector uses placement new on memory blocks. It constructs the data.after allocating the memory in a second line of code.
This technique would work for you as well. Be careful with placement new as it requires you to call destructors manually as well.
Here is a half-assed route without placement new:
template<typename U>
explicit MyArray( U const& constructFromAnythingElse )
{
AllocateSpace(N); // write this, just allocates space
for (int i = 0; i < N; ++i)
{
Element(i) = T( constructFromAnythingElse );
}
}
with placement new, you have to allocate the memory first, then construct in-place, and then remember to destroy each element at the end.
The above is half-assed compared to a placement new route, because we first construct each element, then build another one, and use operator= to overwrite it.
By making it a template constructor on an arbitrary type, we don't rely on multiple conversion to get multiple levels down into the array. The naive version (where you take a T const&) doesn't work because to construct an array of arrays of arrays of T, the outermost one expects an array of arrays of T as an argument, which expects an array of T as an argument, which expects a T -- there are too many levels of user defined construction going on there.
With the above template constructor, the array of array of array of T accepts any type as a constructor. As does the array of array of T, as does the array of T. Finally, the T is passed in whatever you constructed the outermost array of array of array of T, and if it doesn't like it, you get a compiler error message that is nearly completely unreadable.
Make specialization for arrays containing other arrays. To do this you need some common implementation class to be used in general and specialized MyArray:
Common implementation (I made some fixes for you - see !!! comments):
template <class T, int s>
class myArrayImpl {
public:
T* data;
T& operator[](int i){return data[i];} //!!! const before int not needed
const T& operator[](int i) const {return data[i];} //!!! was missing
myArrayImpl(){
data=new T[s]();
}
myArrayImpl(const myArrayImpl & other){
data=new T[s];
copy(other.data,other.data+s,data);
}
myArrayImpl& operator=(const myArrayImpl& other){
T* olddata = data; // !!! need to store old data
data=new T[s];
copy(other.data,other.data+s,data);
delete [] olddata; //!!! to delete it after copying
return *this;
}
~myArrayImpl(){delete [] data;}
};
Then make general implementation - note the definition of value_type and setAll:
template <class T, int s>
class myArray : private myArrayImpl<T,s> {
typedef myArrayImpl<T,s> Impl;
public:
using Impl::operator[];
myArray() : Impl() {}
typedef T value_type; // !!!
explicit myArray(const value_type& value) {
setAll(value);
}
void setAll(const value_type& value) {
fill(this->data, this->data + s, value);
}
};
And the specialized version for myArray of myArray - see also differences in value_type and setAll:
template <class T, int s1, int s2>
class myArray<myArray<T,s2>,s1> : private myArrayImpl<myArray<T,s2>,s1> {
typedef myArrayImpl<myArray<T,s2>,s1> Impl;
public:
using Impl::operator[];
myArray() : Impl() {}
typedef typename myArray<T,s2>::value_type value_type; // !!!
explicit myArray(const value_type& value) {
setAll(value);
}
void setAll(const value_type& value) {
for_each(this->data, this->data + s1, [value](myArray<T,s2>& v) { v.setAll(value); });
}
};
And usage:
int main() {
myArray<myArray<myArray<int,7>,8>,9> a(7);
std::cout << a[0][0][0] << std::endl;
std::cout << a[8][7][6] << std::endl;
}
Full example here: http://ideone.com/0wdT9D