I wrote a simple B-Tree, with Node is defined as:
class Node {
Node* parent = nullptr;
std::uint32_t index = 0;
std::uint32_t height = 1;
std::vector<T> key;
std::vector<unique_alloc_ptr<Node>> child;
// ... details ...
};
where unique_alloc_ptr is the unique_ptr using my custom allocator.
template <typename T>
using unique_alloc_ptr = std::unique_ptr<T, std::function<void(T*)>>;
template <typename T>
unique_alloc_ptr<T> make_unique_fixed(FixedAllocator<T>& alloc) {
T* ptr = alloc.allocate(1);
alloc.construct(ptr);
std::function<void(T*)> deleter;
auto deleter_ = [](T* p, FixedAllocator<T>& alloc) {
alloc.destroy(p);
alloc.deallocate(p, 1);
};
deleter = [&deleter_, &alloc](auto&& PH1) { return deleter_(std::forward<decltype(PH1)>(PH1), alloc); };
return std::unique_ptr<T, decltype(deleter)>(ptr, deleter);
}
The custom allocator uses the memory pool as:
template <typename T>
class FixedAllocator {
struct Chunk {
// details ...
unsigned char data_[blockSize_ * numBlocks_];
};
// details ...
std::vector<Chunk> chunks_;
and my B-Tree uses the memory pool as member variable:
class BTree {
class Node {
// ...
};
// details ...
FixedAllocator<Node> alloc;
unique_alloc_ptr<Node> root;
};
But this gives segfault. As I guess, double free is the problem.
When lifetime of BTree ends, FixedAllocator<Node> is destroyed,
and its internal buffer std::vector<Chunk> is also destroyed.
The problem is, unique_alloc_ptr<Node> is also destroyed as well,
calling destructor of std::vector<unique_alloc_ptr<Node>> child,
which uses FixedAllocator<Node> as internal memory pool,
so double free problem occurs.
How can I solve this problem?
EDIT: Detailed implementation of FixedAllocator
template <typename T>
class FixedAllocator {
struct Chunk {
static constexpr std::size_t blockSize_ = sizeof(T);
static constexpr unsigned char numBlocks_ = FixedAllocator::numBlocks_;
Chunk() {
unsigned char i = 0;
for (unsigned char * p = &data_[0]; i != numBlocks_; p += blockSize_) {
*p = ++i;
}
}
void* allocate() {
unsigned char* result = &data_[firstAvailableBlock_ * blockSize_];
firstAvailableBlock_ = *result;
--blocksAvailable_;
return result;
}
void deallocate(void* p) {
assert(p >= &data_[0]);
auto* toRelease = static_cast<unsigned char*>(p);
assert((toRelease - &data_[0]) % blockSize_ == 0);
*toRelease = firstAvailableBlock_;
firstAvailableBlock_ = static_cast<unsigned char>((toRelease - &data_[0]) / blockSize_);
assert(firstAvailableBlock_ == (toRelease - &data_[0]) / blockSize_);
++blocksAvailable_;
}
bool hasBlock(void* p, std::size_t chunkLength) const {
auto* pc = static_cast<unsigned char*>(p);
return (&data_[0] <= pc) && (pc < &data_[chunkLength]);
}
[[nodiscard]] bool hasAvailable() const {
return (blocksAvailable_ == numBlocks_);
}
[[nodiscard]] bool isFilled() const {
return !blocksAvailable_;
}
unsigned char data_[blockSize_ * numBlocks_];
unsigned char firstAvailableBlock_ = 0;
unsigned char blocksAvailable_ = numBlocks_;
};
private:
static constexpr std::size_t blockSize_ = sizeof(T);
static constexpr unsigned char numBlocks_ = std::numeric_limits<unsigned char>::max();
std::vector<Chunk> chunks_;
Chunk* allocChunk_ = nullptr;
Chunk* deallocChunk_ = nullptr;
Chunk* emptyChunk_ = nullptr;
public:
using value_type = T;
void* doAllocate() {
if (!allocChunk_ || !allocChunk_->blocksAvailable_) {
auto it = chunks_.begin();
for (; ; ++it) {
if (it == chunks_.end()) {
chunks_.emplace_back();
allocChunk_ = &chunks_.back();
deallocChunk_ = &chunks_.front();
break;
}
if (it->blocksAvailable_) {
allocChunk_ = &*it;
break;
}
}
}
assert(allocChunk_);
assert(allocChunk_->blocksAvailable_);
return allocChunk_->allocate();
}
value_type* allocate(std::size_t n) {
assert(n == 1);
auto* p = static_cast<value_type*>(doAllocate());
return p;
}
Chunk* findVicinity(void* p) const {
assert(!chunks_.empty() && deallocChunk_);
const std::size_t chunkLength = numBlocks_ * blockSize_;
// bidirectional search
Chunk* lo = deallocChunk_;
Chunk* hi = deallocChunk_ + 1;
const Chunk* lbound = &chunks_.front();
const Chunk* hbound = &chunks_.back() + 1;
if (hi == hbound) {
hi = nullptr;
}
for (;;) {
if (lo) {
if (lo->hasBlock(p, chunkLength)) {
return lo;
}
if (lo == lbound) {
lo = nullptr;
if (!hi) {
break;
}
} else {
--lo;
}
}
if (hi) {
if (hi->hasBlock(p, chunkLength)) {
return hi;
}
if (++hi == hbound) {
hi = nullptr;
if (!lo) {
break;
}
}
}
}
return nullptr;
}
void deallocate(void* p, std::size_t n) noexcept {
assert(n == 1);
assert(!chunks_.empty());
assert(&chunks_.front() <= deallocChunk_);
assert(&chunks_.back() >= deallocChunk_);
assert(&chunks_.front() <= allocChunk_);
assert(&chunks_.back() >= allocChunk_);
Chunk* foundChunk = nullptr;
const std::size_t chunkLength = numBlocks_ * blockSize_;
if (deallocChunk_->hasBlock(p, chunkLength)) {
foundChunk = deallocChunk_;
} else {
foundChunk = findVicinity(p);
}
assert(foundChunk && foundChunk->hasBlock(p, chunkLength));
deallocChunk_ = foundChunk;
// double free check
assert(emptyChunk_ != deallocChunk_);
assert(!deallocChunk_->hasAvailable());
assert(!emptyChunk_ || emptyChunk_->hasAvailable());
deallocChunk_->deallocate(p);
if (deallocChunk_->hasAvailable()) {
// only release chunk if there are 2 empty chunks.
if (emptyChunk_) {
// if last chunk is empty, just let deallocChunk_ points
// to empty chunk, and release the last.
// otherwise, swap two and release an empty chunk
Chunk* lastChunk = &chunks_.back();
if (lastChunk == deallocChunk_) {
deallocChunk_ = emptyChunk_;
} else if (lastChunk != emptyChunk_) {
std::swap(*emptyChunk_, *lastChunk);
}
assert(lastChunk->hasAvailable());
chunks_.pop_back();
if ((allocChunk_ == lastChunk) || (allocChunk_->isFilled())) {
allocChunk_ = deallocChunk_;
}
}
emptyChunk_ = deallocChunk_;
}
}
template <typename... Args>
void construct (value_type* p, Args&&... args) {
std::construct_at(p, std::forward<Args>(args)...);
}
void destroy(value_type* p) {
std::destroy_at(p);
}
};
Related
While creating a custom class for STL Multimap, I came across an unintended behaviour where dynamic arrays created by new operator are not of the size between []. In the following code, in a.Set(3, 'c') the arrays stored in newKey and newSize are of size one, when they should have size two. Using the debugger shows that in that lines index is equal to 1, so size should be two. Program does not produce any exception but it also does not output the expected result c.
As clarification, using the debugger shows that the problem occurs while setting the value at index 1 in newKey, newSize, newValue. It does not throw any kind of exception, but does not change any value either.
template<typename T>
void Copy(T const* _source, T* _destiny, unsigned long _size)
{
for (unsigned long i = 0; i < _size; i++)
{
_destiny[i] = _source[i];
}
}
template<typename T>
void CopyNew(T const* _source, T* _destiny, unsigned long _size)
{
T* target = new T[_size];
for (unsigned long i = 0; i < _size; i++)
{
target[i] = _source[i];
}
_destiny = target;
}
template<typename T1, typename T2>
class Multimap
{
public:
Multimap() {}
unsigned long Get(T1 const& _key, T2** _return)
{
for (unsigned long i = 0; i < this->keySize_; i++)
{
if (_key == this->key_[i])
{
CopyNew<T2>(this->value_[i], *_return, this->valueSize_[i]);
return i;
}
}
*_return = 0;
return this->keySize_;
}
unsigned long Get(T1 const& _key)
{
for (unsigned long i = 0; i < this->keySize_; i++)
{
if (_key == this->key_[i])
{
return i;
}
}
return this->keySize_;
}
int Set(T1 const& _key, T2 const& _value)
{
T2* target;
unsigned long index = this->Get(_key, &target);
if (target == 0)
{
T1* newKey = new T1[index + 1];
unsigned long* newSize = new unsigned long[index + 1];
T2** newValue = new T2*[this->keySize_ + 1];
if (this->keySize_ != 0)
{
Copy(this->key_, newKey, index);
delete[] this->key_;
Copy(this->valueSize_, newSize, index);
for (unsigned long i = 0; i < this->keySize_; i++)
{
newValue[i] = new T2[this->valueSize_[i]];
Copy(this->value_[i], newValue[i], this->valueSize_[i]);
delete[] this->value_[i];
}
delete[] this->valueSize_;
}
newKey[index] = _key;
newSize[index] = 0;
this->key_ = newKey;
this->valueSize_ = newSize;
this->value_ = newValue;
this->keySize_++;
}
unsigned long newSize = this->valueSize_[index]+1;
T2* newValue = new T2[newSize];
Copy(this->value_[index], newValue, newSize-1);
newValue[newSize-1] = _value;
this->valueSize_[index] = newSize;
this->value_[index] = newValue;
return newSize;
}
unsigned int GetSize()
{
return this->keySize_;
}
protected:
unsigned long keySize_ = 0;
unsigned long* valueSize_ = 0;
T1* key_ = 0;
T2** value_ = 0;
};
int main()
{
Multimap<int, char> a;
a.Set(2, 'b');
a.Set(3, 'c');
char* b;
a.Get(3, &b);
std::cout << b[0];
}
CopyNew argument _destiny should be T*& (as pointed out by WhozCraig in the comments). Otherwise, the function is changing the argument but not the variable passed to the function. In order to change the variable, you have to de-reference the argument, so its type has to be either a pointer or a reference to the variable type. Since the type of the variable is T*, the argument type should be T** or T*&
from the main.cpp
assert error is here
if(!((CPU1.peek()).isExecuting())) {
cout << "Running: " << CPU1.peek().running << endl;
(CPU1.peek()).execute();
cout << "Running: " << CPU1.peek().running << endl;
assert((CPU1.peek()).isExecuting());
((PCB)CPU1.peek()).setStart(slice);
((PCB)CPU1.peek()).setWait(slice-((PCB)CPU1.peek()).getArrival());
averageWait1 += ((PCB)CPU1.peek()).getWait();
cpu1PCBTotal++;
length1 = ((PCB)CPU1.peek()).getLength();
}
PCB.h
class PCB
{
private:
/**
* this process ID
*/
int pid;
/**
* the nice (priority) value of this process
*/
int priority;
/**
* running status 0=idle 1=running
*/
int running;
public:
/**
* Creates a simulated job with default values for its parameters.
*/
PCB()
{
priority = 19;
running = 0;
arrived = 0;
length = 0;
}
PCB(int id, int pval, int run, int arr, int len)
{
pid = id;
priority = pval;
running = run;
arrived = arr;
length = len;
}
bool isExecuting() const
{
return (running == 1);
}
void execute()
{
running = 1;
}
heap.cpp
template <typename E>
Heap<E>::Heap()
{
// compiler-generated code .. no need to implement this
}
template <typename E>
Heap<E>::~Heap()
{
while(tree.size() > 0)
tree.pop_back();
}
template <typename E>
bool Heap<E>::isEmpty() const
{
return tree.size() == 0;
}
template<typename E>
void Heap<E>::insert(E item)
{
tree.push_back(item);
int place = size()-1;
int parent = (place-1)/2;
while(parent >= 0 && tree[place] > tree[parent]) {
swap(place, parent);
place = parent;
parent = (place-1)/2;
}
}
template<typename E>
E Heap<E>::remove() throw (HeapException)
{
E root = tree[0];
tree[0] = tree[size()-1];
tree.pop_back();
reheapify(0);
return root;
}
template<typename E>
const E& Heap<E>::peek() const throw (HeapException)
{
return tree[0];
}
template<typename E>
int Heap<E>::size()const
{
return tree.size();
}
template<typename E>
void Heap<E>::swap(int place, int parent)
{
E temp = tree[place];
tree[place] = tree[parent];
tree[parent] = temp;
}
template<typename E>
void Heap<E>::reheapify(int root)
{
int l = root*2+1;
int r = root*2+2;
if(l < size()-1) {
if(r < size()-1)
if(tree[root] < tree[r]) {
swap(root, r);
reheapify(r);
}
if(tree[root] < tree[l]) {
swap(root, l);
reheapify(l);
}
}
}
The assertion is failing, and I have no idea why... Please help, thanks
Top is main function, bottom is PCB.h; CPU class is a Heap
Originally typecasted CPU1.peek() because it gave me the error "passing 'const PCB' as 'this' argument discards qualifiers" So I guess I needs help calling CPU1.peek().execute()
I'm developing a container template class. This code needs to interface with existing C code and needs to stay binary compatible, so I can not use i.e. std::vector or similar.
The problem that I have is that it needs to support different allocation strategies, and I don't know how to provide the allocator as a template argument. I created an SSCCE to illustrate how far I got (which of course doesn't compile, because if it would, I wouldn't need to ask this question :)).
#include <iostream>
#include <cstring>
#include <type_traits>
typedef unsigned int uint_t;
typedef signed int int_t;
template <typename T, typename S, typename _allocator = _virtual>
class Container
{
public:
Container(S nItems = 0, S nMaxItems = 0, T *pArray = NULL)
{
mItems = nItems;
mMaxItems = nMaxItems;
mArray = pArray;
}
void adjustMalloc(uint_t nClusterSize)
{
if(mItems == mMaxItems)
{
mArray = (T *)realloc(mArray, (mMaxItems+nClusterSize)*sizeof(T));
mMaxItems += nClusterSize;
}
}
void adjustAligned(uint_t nClusterSize)
{
if(mItems == mMaxItems)
{
mArray = (T *)_aligned_realloc(mArray, (mMaxItems+nClusterSize)*sizeof(T), 16);
mMaxItems += nClusterSize;
}
}
void adjustVirtual(uint_t nClusterSize)
{
if(mItems == mMaxItems)
{
mArray = VirtualAlloc(mArray, (mMaxItems+nClusterSize)*sizeof(T), MEM_RESERVE, PAGE_NOACCESS);
mMaxItems += nClusterSize;
}
}
void adjust(uint_t nClusterSize)
{
if (std::is_same<_allocator>::value == _virtual)
adjustVirtual(nClusterSize);
else if(std::is_same<_allocator>::value == _aligned)
adjustAligned(nClusterSize);
else if(std::is_same<_allocator>::value == _malloc)
adjustMalloc(nClusterSize);
else
{
// Cause a static compiler error, how?
}
}
bool add(T *pItem)
{
if(find(pItem) == NULL)
{
adjust(100);
mItems++;
return true; // added
}
return false;
}
T *find(T *pItem)
{
T *p = mArray;
for(S i = 0; i < mItems; i++, p++)
{
if(*p == *pItem)
return p;
}
return NULL;
}
private:
S mItems;
S mMaxItems;
T *mArray;
};
class Record
{
public:
bool operator==(const Record &oRecord)
{
if(Id != oRecord.Id)
return false;
if(strcmp(Name, oRecord.Name) != 0)
return false;
return true;
}
int Id;
char Name[10+1];
};
int main(int argc, char *argv[])
{
Record rec;
rec.Id = 0;
strcpy(rec.Name, "Test");
Container<Record, uint_t> records; // Default using malloc
records.add(&rec);
if(records.find(&rec) == NULL)
std::cerr << "Not found" << std::endl;
Container<Record, uint_t, _virtual> vrecords; // VirtualAlloc allocator used.
vrecords.add(&rec);
if(records.find(&rec) == NULL)
std::cerr << "Not found" << std::endl;
return 0;
}
I'm using Visual Studio 2010 so it's not 100% C++11.
The VirtualAlloc is provided just as (another) example and will not work as it is shown here.
I found a solution for my problem. However, I get warnings
warning C4127: conditional expression is constant
in the adjust() method for the if(std::is_same... and I was wondering if this is normal or if I can get rid of it, other than disabling it.
#include "stdafx.h"
#include "windows.h"
#include <iostream>
#include <cstring>
#include <type_traits>
#pragma warning (push)
//#pragma warning (disable : 4127)
typedef unsigned int uint_t;
typedef signed int int_t;
typedef struct { const static bool _virtual_allocator = true; } _virtual_type;
typedef struct { const static bool _aligned_allocator = true; } _aligned_type;
typedef struct { const static bool _malloc_allocator = true; } _malloc_type;
template <typename T, typename S, typename _allocator = _aligned_type>
class Container
{
public:
Container(S nItems = 0, S nMaxItems = 0, T *pArray = NULL)
{
mItems = nItems;
mMaxItems = nMaxItems;
mArray = pArray;
}
void adjustMalloc(uint_t nClusterSize)
{
if(mItems == mMaxItems)
{
mArray = (T *)realloc(mArray, (mMaxItems+nClusterSize)*sizeof(T));
mMaxItems += nClusterSize;
}
}
void adjustAligned(uint_t nClusterSize)
{
if(mItems == mMaxItems)
{
mArray = (T *)_aligned_realloc(mArray, (mMaxItems+nClusterSize)*sizeof(T), 16);
mMaxItems += nClusterSize;
}
}
void adjustVirtual(uint_t nClusterSize)
{
if(mItems == mMaxItems)
{
mArray = (T *)VirtualAlloc((LPVOID)mArray, (mMaxItems+nClusterSize)*sizeof(T), MEM_RESERVE, PAGE_NOACCESS);
mMaxItems += nClusterSize;
}
}
void adjust(uint_t nClusterSize)
{
if (std::is_same<_allocator, _virtual_type>::value)
adjustVirtual(nClusterSize);
else if(std::is_same<_allocator, _aligned_type>::value)
adjustAligned(nClusterSize);
else if(std::is_same<_allocator, _malloc_type>::value)
adjustMalloc(nClusterSize);
else
{
// Cause a static compiler error, how?
}
}
bool add(T *pItem)
{
if(find(pItem) == NULL)
{
adjust(100);
mItems++;
return true; // added
}
return false;
}
T *find(T *pItem)
{
T *p = mArray;
for(S i = 0; i < mItems; i++, p++)
{
if(*p == *pItem)
return p;
}
return NULL;
}
private:
S mItems;
S mMaxItems;
T *mArray;
};
#pragma warning (pop)
class Record
{
public:
bool operator==(const Record &oRecord)
{
if(Id != oRecord.Id)
return false;
if(strcmp(Name, oRecord.Name) != 0)
return false;
return true;
}
int Id;
char Name[10+1];
};
int main(int argc, char *argv[])
{
Record rec;
rec.Id = 0;
strcpy(rec.Name, "Test");
Container<Record, uint_t> mrecords;
mrecords.add(&rec);
if(mrecords.find(&rec) == NULL)
std::cerr << "Malloc Not found" << std::endl;
Container<Record, uint_t, _aligned_type> arecords;
arecords.add(&rec);
if(arecords.find(&rec) == NULL)
std::cerr << "Aligned Not found" << std::endl;
Container<Record, uint_t, _virtual_type> vrecords;
vrecords.add(&rec);
if(vrecords.find(&rec) == NULL)
std::cerr << "Virtual Not found" << std::endl;
return 0;
}
Having an issue when trying to pass a const reference value within a range based for loop to a
function that takes a const reference value. Look at commments within the range based for loop to see what I'm talking about. It doesn't seem to be an issue with my implementation of 'unordered_vector' which is what I thought at first since it prints correctly outside the function. Why does this fail? It works if I pass by value, so I'm a bit confused here. Looks like it is referencing the reference which doesn't seem to be correct behaviour.
void println(const vec3& p)
{
std::cout << '<' << p.x << ',' << p.y << ',' << p.z << '>' << std::endl;
}
void printlnvalue(vec3 p)
{
std::cout << '<' << p.x << ',' << p.y << ',' << p.z << '>' << std::endl;
}
const int vector_length = 7;
int _tmain(int argc, _TCHAR* argv[])
{
unordered_vector<vec3> vectors(vector_length);
for (int i = 0; i < vector_length; ++i)
{
vectors.push_back(vec3(0.1f*i, 0.2f*i, 0.3f*i));
}
for (const auto& i : vectors)
{
//Prints correctly
printlnvalue(i);
//Function that does same thing but doesn't print correctly.
println(i);
}
return 0;
}
Output
<0,0,0>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.1,0.2,0.3>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.2,0.4,0.6>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.3,0.6,0.9>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.4,0.8,1.2>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.5,1,1.5>
<8.68805E-044,2.66467E-038,4.12969E-039>
<0.6,1.2,1.8>
<8.68805E-044,2.66467E-038,4.12969E-039>
unordered_vector declaration
template<typename T>
class unordered_vector
{
private:
T* m_data;
size_t m_size;
size_t m_capacity;
public:
typedef unordered_vector_iterator<T> iterator;
typedef unordered_vector_iterator<T> const const_iterator;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef T value_type;
typedef T* pointer;
typedef T& reference;
size_t size(void) const;
size_t capacity(void) const;
bool empty(void) const;
void resize(size_t count);
void reserve(size_t count);
void push_back(const T& val);
const_iterator begin(void) const;
const_iterator end(void) const;
void shrink_to_fit(void);
void erase(size_t i);
void erase(const T& val);
void erase_all(const T& val);
T& operator[](size_t i);
T operator[](size_t i) const;
unordered_vector& operator=(const unordered_vector& copy);
unordered_vector& operator=(unordered_vector&& mov);
unordered_vector(void);
unordered_vector(size_t _size);
unordered_vector(unordered_vector& copy);
unordered_vector(unordered_vector&& mov);
~unordered_vector(void);
};
unordered_vector definition
template<typename T>
/*
Returns the number of active units within
the unordered_vector.
*/
size_t unordered_vector<T>::size(void) const
{
return m_size;
}
template<typename T>
/*
Returns the potential size of the vector
before it would have to resize.
*/
size_t unordered_vector<T>::capacity(void) const { return m_capacity; }
template<typename T>
/*
Returns true if no active units are within the vector. size() == 0
*/
bool unordered_vector<T>::empty(void) const
{
return m_size == 0;
}
template<typename T>
/*
This resizes the vector where anything between the parameter and
size is part of the capacity.
*/
void unordered_vector<T>::reserve(size_t count)
{
if (count > m_capacity)
{
//Set capacity to new value
m_capacity = count;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
//Copy preexisting data byte for byte
if (m_data != nullptr)
{
//Copy data from previous buffer to new buffer
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
free(m_data);
}
//Set pointer to the new array
m_data = new_ptr;
}
}
template<typename T>
/*
Will resize the vector but each of the units is
active if the buffer is increased. If the size
is reduced then items at the end are truncated.
*/
void unordered_vector<T>::resize(size_t count)
{
if (count > m_capacity)
{
//Set capacity to new value
m_capacity = count;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
//Copy preexisting data byte for byte
if (m_data != nullptr)
{
//Copy data from previous buffer to new buffer
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
free(m_data);
}
//Set pointer to the new array
m_data = new_ptr;
//Make default values at each location
for (; m_size < count; ++m_size)
{
}
}
else if (count < m_capacity)
{
if (count < m_size) m_size = count;
//Set capacity to new value
m_capacity = count;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
//Copy preexisting data byte for byte
if (m_data != nullptr)
{
//Copy data from previous buffer to new buffer
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
free(m_data);
}
//Set pointer to the new array
m_data = new_ptr;
}
}
template<typename T>
/*
Shrinks capacity so capacity() == size().
*/
void unordered_vector<T>::shrink_to_fit(void)
{
if (m_size != m_capacity && m_data != nullptr)
{
m_capacity = m_size;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
//Copy preexisting data byte for byte
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
if (m_data != nullptr)
free(m_data);
//Set pointer to the new array
m_data = new_ptr;
}
}
template<typename T>
/*
Will copy a value into the next open space of the vector.
If there isn't room for an additional item it will resize
the vector to accomadate it.
*/
void unordered_vector<T>::push_back(const T& val)
{
//If we don't have any more room we need
//to resize the array
if (m_size == m_capacity)
{
m_capacity = m_capacity == 0 ? 8 : m_capacity * 2;
//Make new array
T* new_ptr = static_cast<T*>(malloc(sizeof(T)*m_capacity));
if (m_data != nullptr)
{
//Copy preexisting data byte for byte
memcpy(new_ptr, m_data, sizeof(T)*m_size);
//Delete previous buffer
free(m_data);
}
//Set pointer to the new array
m_data = new_ptr;
}
m_data[m_size++] = val;
}
template<typename T>
/*
Random accessor for writing to array.
*/
T& unordered_vector<T>::operator[](size_t i)
{
assert(i >= 0 && i < m_size);
return m_data[i];
}
template<typename T>
/*
Random accessor for reading from array.
*/
T unordered_vector<T>::operator[](size_t i) const
{
assert(i >= 0 && i < m_size);
return m_data[i];
}
template<typename T>
/*
Constant time erase. It reorders the vector
internally to allow this.
*/
void unordered_vector<T>::erase(size_t i)
{
assert(i >= 0 && i < m_size);
if (i == m_size - 1)
m_size -= 1;
else
{
m_data[i] = m_data[m_size - 1];
m_size -= 1;
}
}
template<typename T>
/*
Will remove the first value that matches
val if it exists. In the event no value is
found the request is ignored.
*/
void unordered_vector<T>::erase(const T& val)
{
for (size_t i = 0; i < m_size; ++i)
{
if (m_data[i] == val)
{
if (i == m_size - 1)
m_size -= 1;
else
{
m_data[i] = m_data[m_size - 1];
m_size -= 1;
}
break;
}
}
}
template<typename T>
/*
Will remove all values that match the parameter.
If no items match the parameter then the request
is ignored.
*/
void unordered_vector<T>::erase_all(const T& val)
{
for (size_t i = 0; i < m_size; ++i)
{
if (m_data[i] == val)
{
if (i == m_size - 1)
m_size -= 1;
else
{
m_data[i] = m_data[m_size - 1];
m_size -= 1;
//Haven't checked the back yet
//so we need to recheck i
i--;
}
}
}
}
template<typename T>
/*
Initializes the vector with 0 capacity.
*/
unordered_vector<T>::unordered_vector(void)
: m_data(nullptr), m_size(0), m_capacity(0) {}
template<typename T>
/*
Initializes the vector with a capacity() equal to
the parameter.
*/
unordered_vector<T>::unordered_vector(size_t count)
: m_data(nullptr), m_size(0), m_capacity(0)
{
reserve(count);
}
template<typename T>
/*
Will copy data from another unordered_vector.
*/
unordered_vector<T>::unordered_vector(unordered_vector<T>& vector)
{
if (vector.m_capacity > 0)
{
m_data = malloc(sizeof(T)*vector.m_capacity);
if (vector.m_size > 0)
memcpy(m_data, vector.m_data, sizeof(T)*vector.m_size);
}
m_capacity = vector.m_capacity;
m_size = vector.m_size;
}
template<typename T>
/*
Move constructor to effeciently transfer data between
a temporary and another unordered_vector.
*/
unordered_vector<T>::unordered_vector(unordered_vector<T>&& mov)
{
m_data = mov.m_data;
m_capacity = mov.m_capacity;
m_size = mov.m_size;
mov.m_data = nullptr;
}
template<typename T>
/*
Destructor that deallocates memory
if any was allocated. Will not deallocate
memory if move semantic was invoked.
*/
unordered_vector<T>::~unordered_vector(void)
{
if (m_data != nullptr)
{
free(m_data);
m_data = nullptr;
}
m_size = 0;
m_capacity = 0;
}
template<typename T>
unordered_vector<T>& unordered_vector<T>::operator=(const unordered_vector<T>& copy)
{
if (m_data != nullptr)
{
free(m_data);
m_data = nullptr;
}
if (copy.m_capacity > 0)
{
m_data = malloc(sizeof(T)*copy.capacity());
if (copy.m_size > 0)
memcpy(m_data, copy.m_data, sizeof(T)*copy.m_size)
}
m_capacity = copy.m_capacity;
m_size = copy.m_size;
return *this;
}
template<typename T>
unordered_vector<T>& unordered_vector<T>::operator=(unordered_vector<T>&& mov)
{
if (m_data != nullptr)
free(m_data);
m_data = mov.m_data;
m_capacity = mov.m_capacity;
m_size = mov.m_size;
mov.m_data = nullptr;
}
template<typename T>
unordered_vector_iterator<T> const unordered_vector<T>::begin(void) const
{
return unordered_vector_iterator<T>(this, 0);
}
template<typename T>
unordered_vector_iterator<T> const unordered_vector<T>::end(void) const
{
return unordered_vector_iterator<T>(this, m_size);
}
iterator declaration
template<typename T>
class unordered_vector_iterator
{
private:
const unordered_vector<T>* const m_container;
size_t m_index;
public:
unordered_vector_iterator& operator++(void);
unordered_vector_iterator& operator++(int);
unordered_vector_iterator& operator--(void);
unordered_vector_iterator& operator--(int);
T& operator*(void) const;
T& operator->(void) const;
bool operator==(const unordered_vector_iterator& iter) const;
bool operator!=(const unordered_vector_iterator& iter) const;
unordered_vector_iterator(const unordered_vector<T>* const container, size_t index)
: m_container(container), m_index(index) {}
};
iterator definition
template<typename T>
bool unordered_vector_iterator<T>::operator==(const unordered_vector_iterator<T>& iter) const
{
return iter.m_index == m_index && iter.m_container == m_container;
}
template<typename T>
bool unordered_vector_iterator<T>::operator!=(const unordered_vector_iterator<T>& iter) const
{
return iter.m_index != m_index || iter.m_container != m_container;
}
template<typename T>
unordered_vector_iterator<T>& unordered_vector_iterator<T>::operator++(void)
{
++m_index;
return *this;
}
template<typename T>
unordered_vector_iterator<T>& unordered_vector_iterator<T>::operator++(int)
{
++m_index;
return *this;
}
template<typename T>
unordered_vector_iterator<T>& unordered_vector_iterator<T>::operator--(void)
{
--m_index;
return *this;
}
template<typename T>
unordered_vector_iterator<T>& unordered_vector_iterator<T>::operator--(int)
{
--m_index;
return *this;
}
template<typename T>
T& unordered_vector_iterator<T>::operator*(void) const
{
return (*m_container)[m_index];
}
template<typename T>
T& unordered_vector_iterator<T>::operator->(void) const
{
return (*m_container)[m_index];
}
vec3 declaration
struct vec3
{
public:
float x, y, z;
//vector functions
void normalize(void);
float magnitude(void) const;
float magnitudeSq(void) const;
bool operator==(vec3 o) const;
bool operator!=(vec3 o) const;
//Constructors
vec3(float _x, float _y, float _z)
: x(_x), y(_y), z(_z) {}
vec3(float s)
: x(s), y(s), z(s) {}
vec3()
: x(0.f), y(0.f), z(0.f) {}
};
You might need to declare another operator in unordered_vector to return a const T&. That's how it's declared in the STL vector class:
typedef const_reference const T&;
const_reference operator[](size_t idx) const;
The issue with the code is that the const method does not return a reference value.
Also for the code to work it has to return a non-constant reference value for some reason. Not a great answer but it works flawlessly now by changing the const index operator to this.
template<typename T>
/*
Random accessor for reading from array.
*/
T& unordered_vector<T>::operator[](size_t i) const
{
assert(i >= 0 && i < m_size);
return m_data[i];
}
I have this class:
class HIDValue{
private:
void* mValue;
UINT mSize;
HIDElement mElement;
public:
HIDValue() {
mValue = 0;
mSize = 0;
}
template <class T>
HIDValue(T pValue, HIDElement pElement) {
mElement = pElement;
mValue = 0;
setValue(pValue);
}
HIDValue(const HIDValue& pValue) {
mSize = pValue.mSize;
mElement = pValue.mElement;
mValue = 0;
if(mSize) {
mValue = new char[mSize];
memcpy(mValue, pValue.mValue, mSize);
}
}
template <class T>
void setValue(T pValue) {
if(mValue)
delete mValue;
mValue = new T;
*((T*)mValue) = *((T*)&pValue);
mSize = sizeof(T);
}
~HIDValue() {
//THE MENTIONED ERROR IS HERE
if(mValue)
delete mValue;
}
void setElement(HIDElement pElement) {
mElement = pElement;
}
const HIDElement& getElement() const {
return mElement;
}
template <class T>
bool getValue(T* pValue) const {
if(mValue && mSize <= sizeof(T)) {
*pValue = *((T*)mValue);
return true;
}
return false;
}
};
With some changes it's working fine:
class HIDValue{
private:
//void* mValue;
char mValue[16];
UINT mSize;
HIDElement mElement;
public:
HIDValue() {
//mValue = 0;
mSize = 0;
}
template <class T>
HIDValue(T pValue, HIDElement pElement) {
mElement = pElement;
//mValue = 0;
setValue(pValue);
}
HIDValue(const HIDValue& pValue) {
mSize = pValue.mSize;
mElement = pValue.mElement;
//mValue = 0;
if(mSize) {
//mValue = new char[mSize];
memcpy(mValue, pValue.mValue, mSize);
}
}
template <class T>
void setValue(T pValue) {
//if(mValue)
// delete mValue;
//mValue = new T;
*((T*)mValue) = *((T*)&pValue);
mSize = sizeof(T);
}
~HIDValue() {
//itten egy hiba vala
//if(mValue)
// delete mValue;
}
void setElement(HIDElement pElement) {
mElement = pElement;
}
const HIDElement& getElement() const {
return mElement;
}
template <class T>
bool getValue(T* pValue) const {
if(mValue && mSize <= sizeof(T)) {
*pValue = *((T*)mValue);
return true;
}
return false;
}
};
I am curious what is the reason of the error.
Thanks ahead and sorry for my english!
Don't use void*. You want to use Boost.Any or Boost.Variant (latter if the set of allowed types is to be restricted).
Don't use memcpy. It's likely to not work correctly with UDTs.
Don't use C-style casts.
The error is most likely related to the fact that you're deleting void* instead of T*.
You are trying to delete a pointer of type void which can cause many problems. Also, when you delete a void pointer the destructors of the mValue don't fire because the type of the object is unknown.