C++ CycleBuffer unit test fail reason? - c++
I have implement my CycleBuffer and run some unit tests. But it fails on a random read/write operation and I cannot find out why. Please help me. I use C++ 14 standard.
My CycleBuffer rely on libfmt and Catch2.
CycleBuffer.h
// Copyright 2019- <shepherd-lang>
// Apache License Version 2.0
#pragma once
#include <cstdio>
namespace detail {
/**
* CycleBuffer memory status
*
* positive: tail >= head
* 1)
* buf = head = tail = nullptr
*
* 2)
* head tail
* | |
* |-----------------------||
* |
* buf
*
* 3)
* head
* |
* |---------||-------------|
* | |
* buf tail
*
* 4)
* head tail
* | |
* |----|---------------|---|
* |
* buf
*
* negative: tail < head
* 1)
* tail head
* | |
* |----|---------------|---|
* |
* buf
*
* 2)
* tail head
* | |
* |----------------|-------|
* |
* buf
*
* 3)
* tail head
* | |
* |----|------------------||
* |
* buf
*
* 4)
* tail head
* | |
* |-----------------------||
* |
* buf
*
* impossible)
* tail head
* | |
* |------------------------|
* |
* buf
*/
template <unsigned int D> class CycleBuffer {
public:
CycleBuffer();
virtual ~CycleBuffer();
virtual int capacity() const;
virtual int size() const;
virtual bool empty() const;
virtual bool full() const;
virtual void reset();
// next position start from <position>
virtual char *next(char *position, int distance = 1) const;
virtual const char *next(const char *position, int distance = 1) const;
// previous position start from <position>
virtual char *prev(char *position, int distance = 1) const;
virtual const char *prev(const char *position, int distance = 1) const;
virtual char *begin();
virtual const char *begin() const;
virtual char *rbegin();
virtual const char *rbegin() const;
virtual char *end();
virtual const char *end() const;
virtual char *rend();
virtual const char *rend() const;
virtual bool contain(const char *position) const;
virtual std::string toString() const;
// write at most <n> bytes to <buf>
// #return bytes really write
virtual int write(char *buf, int n);
// read at most <n> bytes from <buf>
// #return bytes really read
virtual int read(const char *buf, int n);
// write all bytes to <fp>
// #return bytes really write
virtual int writefile(FILE *fp);
// write at most <n> bytes to <fp>
virtual int writefile(FILE *fp, int n);
// read all bytes from <fp>
// #return bytes really read
virtual int readfile(FILE *fp);
// read at most <n> bytes from <fp>
// #return bytes really read
virtual int readfile(FILE *fp, int n);
protected:
virtual char *nextImpl(char *position, int distance = 1) const;
virtual char *prevImpl(char *position, int distance = 1) const;
virtual void release();
virtual int expand(int n);
virtual char *bufEnd();
virtual const char *bufEnd() const;
// if positive direction
virtual bool positive() const;
// positive direction
virtual long pSize() const;
virtual bool pContain(const char *position) const;
// negative direction
virtual long nLeftSize() const;
virtual long nRightSize() const;
virtual bool nLeftContain(const char *position) const;
virtual bool nRightContain(const char *position) const;
virtual int writeImpl(void *src, int n,
int (*writeHandler)(void *, void *, int));
virtual int readImpl(void *src, int n,
int (*readHandler)(void *, void *, int, int));
char *buf_;
char *head_;
char *tail_;
int capacity_;
};
} // namespace detail
class DynamicBuffer : public detail::CycleBuffer<1> {
public:
DynamicBuffer(int capacity = 0);
virtual ~DynamicBuffer() = default;
virtual std::string toString() const;
};
class FixedBuffer : public detail::CycleBuffer<0> {
public:
FixedBuffer(int capacity);
virtual ~FixedBuffer() = default;
virtual std::string toString() const;
};
CycleBuffer.cpp
// Copyright 2019- <shepherd-lang>
// Apache License Version 2.0
#include "CycleBuffer.h"
#include "fmt/format.h"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <tuple>
#define ALIGN(n) (n < 8 ? 8 : (n % 8 == 0 ? n : ((n / 8 + 1) * 8)))
#define MIN(a, b) (std::min<int>(a, b))
#define BUF_SIZE 1024
namespace detail {
template <unsigned int D> char *CycleBuffer<D>::bufEnd() {
return buf_ + capacity_;
}
template <unsigned int D> const char *CycleBuffer<D>::bufEnd() const {
return buf_ + capacity_;
}
// is positive direction
template <unsigned int D> bool CycleBuffer<D>::positive() const {
return tail_ >= head_;
}
template <unsigned int D> long CycleBuffer<D>::pSize() const {
return tail_ - head_;
}
template <unsigned int D>
bool CycleBuffer<D>::pContain(const char *position) const {
return position >= head_ && position < tail_;
}
template <unsigned int D> long CycleBuffer<D>::nLeftSize() const {
return tail_ - buf_;
}
template <unsigned int D> long CycleBuffer<D>::nRightSize() const {
return bufEnd() - head_;
}
template <unsigned int D>
bool CycleBuffer<D>::nLeftContain(const char *position) const {
return position >= buf_ && position < tail_;
}
template <unsigned int D>
bool CycleBuffer<D>::nRightContain(const char *position) const {
return position >= head_ && position < bufEnd();
}
template <unsigned int D>
CycleBuffer<D>::CycleBuffer()
: buf_(nullptr), head_(nullptr), tail_(nullptr), capacity_(0) {}
template <unsigned int D> CycleBuffer<D>::~CycleBuffer() { reset(); }
template <unsigned int D> int CycleBuffer<D>::capacity() const {
return capacity_ > 0 ? capacity_ - 1 : 0;
}
template <unsigned int D> int CycleBuffer<D>::size() const {
return positive() ? pSize() : (nLeftSize() + nRightSize());
}
template <unsigned int D> bool CycleBuffer<D>::empty() const {
return size() == 0;
}
template <unsigned int D> bool CycleBuffer<D>::full() const {
return size() == capacity();
}
template <unsigned int D> void CycleBuffer<D>::reset() {
release();
head_ = nullptr;
tail_ = nullptr;
capacity_ = 0;
}
template <unsigned int D>
char *CycleBuffer<D>::nextImpl(char *position, int distance) const {
if (position == end() || !position) {
return (char *)end();
}
char *np = position + distance;
if (positive()) {
return np > tail_ ? (char *)end() : np;
} else {
if (nLeftContain(position)) {
return np > tail_ ? (char *)end() : np;
} else {
if (np < bufEnd()) {
return np;
}
np = (char *)buf_ + (distance - (bufEnd() - position));
return np > tail_ ? (char *)end() : np;
}
}
}
template <unsigned int D>
char *CycleBuffer<D>::next(char *position, int distance) const {
return nextImpl(position, distance);
}
template <unsigned int D>
const char *CycleBuffer<D>::next(const char *position, int distance) const {
return nextImpl((char *)position, distance);
}
template <unsigned int D>
char *CycleBuffer<D>::prevImpl(char *position, int distance) const {
if (position == rend() || !position) {
return (char *)rend();
}
char *np = position - distance;
if (positive()) {
return np < head_ ? (char *)rend() : np;
} else {
if (nLeftContain(position)) {
if (np >= buf_) {
return np;
}
np = (char *)bufEnd() - (distance - (position - buf_));
return np < head_ ? (char *)rend() : np;
} else {
return np < head_ ? (char *)rend() : np;
}
}
}
template <unsigned int D>
char *CycleBuffer<D>::prev(char *position, int distance) const {
return prevImpl(position, distance);
}
template <unsigned int D>
const char *CycleBuffer<D>::prev(const char *position, int distance) const {
return prevImpl((char *)position, distance);
}
template <unsigned int D> char *CycleBuffer<D>::begin() { return head_; }
template <unsigned int D> const char *CycleBuffer<D>::begin() const {
return head_;
}
template <unsigned int D> char *CycleBuffer<D>::rbegin() {
return tail_ == buf_ ? bufEnd() - 1 : tail_ - 1;
}
template <unsigned int D> const char *CycleBuffer<D>::rbegin() const {
return tail_ == buf_ ? bufEnd() - 1 : tail_ - 1;
}
template <unsigned int D> char *CycleBuffer<D>::end() { return tail_; }
template <unsigned int D> const char *CycleBuffer<D>::end() const {
return tail_;
}
template <unsigned int D> char *CycleBuffer<D>::rend() { return head_ - 1; }
template <unsigned int D> const char *CycleBuffer<D>::rend() const {
return head_ - 1;
}
template <unsigned int D>
bool CycleBuffer<D>::contain(const char *position) const {
return positive() ? pContain(position)
: (nLeftContain(position) || nRightContain(position));
}
template <unsigned int D> std::string CycleBuffer<D>::toString() const {
return fmt::format("buf_:{}, head_:{}, tail_:{}, capacity_:{}", (void *)buf_,
(void *)head_, (void *)tail_, capacity_);
}
template <unsigned int D> void CycleBuffer<D>::release() {
if (buf_) {
std::free(buf_);
buf_ = nullptr;
}
}
static int writeMemHandler(void *src, void *buf, int n) {
std::memcpy(src, buf, n);
return n;
}
static int writeFileHandler(void *src, void *buf, int n) {
size_t r = std::fwrite(buf, 1, n, (FILE *)src);
return (int)r;
}
#define WINC(n) \
do { \
head_ = next(head_, n); \
writen += n; \
} while (0)
template <unsigned int D>
int CycleBuffer<D>::writeImpl(void *src, int n,
int (*writeHandler)(void *, void *, int)) {
EX_ASSERT(n >= 0, "n {} < 0", n);
if (!src || !n) {
return 0;
}
if (empty()) {
return 0;
}
int writen = 0;
if (positive()) {
int fn = MIN(size(), n);
int fnr = writeHandler(src, head_, fn);
WINC(fnr);
} else {
int fn = MIN(bufEnd() - head_, n);
int fnr = writeHandler(src, head_, fn);
WINC(fnr);
if (n > writen) {
int sn = MIN(n - writen, pSize());
int snr = writeHandler(src, head_, sn);
WINC(snr);
}
}
return writen;
}
template <unsigned int D> int CycleBuffer<D>::write(char *buf, int n) {
return writeImpl(buf, n, writeMemHandler);
}
template <unsigned int D> int CycleBuffer<D>::writefile(FILE *fp, int n) {
return writeImpl(fp, n, writeFileHandler);
}
template <unsigned int D> int CycleBuffer<D>::writefile(FILE *fp) {
int n = 0;
int tmp;
do {
tmp = writefile(fp, BUF_SIZE);
n += tmp;
} while (tmp > 0);
return n;
}
#define RINC(n) \
do { \
tail_ += n; \
readn += n; \
} while (0)
static int readMemHandler(void *src, void *buf, int n, int readn) {
char *src2 = (char *)src + readn;
std::memcpy(buf, src2, n);
return n;
}
static int readFileHandler(void *src, void *buf, int n, int readn) {
size_t r = std::fread(buf, 1, n, (FILE *)src);
return (int)r;
}
template <unsigned int D>
int CycleBuffer<D>::readImpl(void *src, int n,
int (*readHandler)(void *, void *, int, int)) {
if (!src || !n) {
return 0;
}
if (D) {
if (capacity() - size() < n) {
int c1 = capacity() + n + 1;
int c2 = capacity() * 2 + 1;
expand(c1 > c2 ? ALIGN(c1) : ALIGN(c2));
}
}
if (full()) {
return 0;
}
int readn = 0;
if (positive()) {
int fn = MIN(bufEnd() - tail_ - (head_ == buf_ ? 1 : 0), n);
int fnr = readHandler(src, tail_, fn, readn);
RINC(fnr);
if (tail_ == bufEnd()) {
tail_ = buf_;
}
if (n > readn && head_ != buf_) {
int sn = MIN(n - readn, head_ - tail_ - 1);
int snr = readHandler(src, tail_, sn, readn);
RINC(snr);
}
} else {
int fn = MIN(n, head_ - tail_ - 1);
int fnr = readHandler(src, tail_, fn, readn);
RINC(fnr);
}
return readn;
}
template <unsigned int D> int CycleBuffer<D>::read(const char *buf, int n) {
return readImpl((void *)buf, n, readMemHandler);
}
template <unsigned int D> int CycleBuffer<D>::readfile(FILE *fp, int n) {
return readImpl(fp, n, readFileHandler);
}
template <unsigned int D> int CycleBuffer<D>::readfile(FILE *fp) {
int n = 0;
int tmp;
do {
tmp = readfile(fp, BUF_SIZE);
n += tmp;
} while (tmp > 0);
return n;
}
template <unsigned int D> int CycleBuffer<D>::expand(int n) {
if (n <= capacity_) {
return 0;
}
char *newbuf = (char *)std::malloc(n);
if (!newbuf) {
return -1;
}
int sz = size();
if (positive()) {
std::memcpy(newbuf, head_, pSize());
} else {
std::memcpy(newbuf, head_, nRightSize());
std::memcpy(newbuf + nRightSize(), buf_, nLeftSize());
}
release();
buf_ = newbuf;
capacity_ = n;
head_ = buf_;
tail_ = buf_ + sz;
return 0;
}
} // namespace detail
DynamicBuffer::DynamicBuffer(int capacity) {
if (capacity > 0) {
expand(ALIGN(capacity + 1));
}
}
std::string DynamicBuffer::toString() const {
return fmt::format("[#DynamicBuffer {}]", detail::CycleBuffer<1>::toString());
}
FixedBuffer::FixedBuffer(int capacity) {
if (capacity > 0) {
// precise capacity
expand(capacity + 1);
}
}
std::string FixedBuffer::toString() const {
return fmt::format("[#FixedBuffer {}]", detail::CycleBuffer<0>::toString());
}
CycleBufferTest.cpp
// Copyright 2019- <shepherd-lang>
// Apache License Version 2.0
#include "CycleBuffer.h"
#include "catch2/catch.hpp"
#include "fmt/format.h"
#include <algorithm>
#include <cstdlib>
#define C_MIN 0
#define C_MAX 100
#define RAND (rand() % (C_MAX + 1))
TEST_CASE("container/CycleBuffer", "[container/CycleBuffer]") {
SECTION("random read/write") {
{
std::vector<char> v;
DynamicBuffer db;
int rc = 0, wc = 0;
for (int i = C_MIN; i < C_MAX; i++) {
int rn = RAND;
std::vector<char> rbuf(rn);
for (int j = 0; j < rn; j++) {
rbuf[j] = (char)rn;
v.push_back(rbuf[j]);
rc++;
LOG_INFO("DynamicBuffer random rbuf[{}]:{}", rc, (int)rbuf[j]);
}
REQUIRE(db.read(rbuf.data(), rn) == rn);
int wn = std::min(RAND, rn);
std::vector<char> wbuf(wn);
REQUIRE(db.write(wbuf.data(), wn) == wn);
for (int j = 0; j < wn; j++) {
LOG_INFO("DynamicBuffer random wbuf[{}]:{}", wc, (int)wbuf[j]);
if (wbuf[j] != v[wc]) {
LOG_INFO("DynamicBuffer random wbuf[{}]:{}", wc, (int)wbuf[j]); // here's the error place!
}
REQUIRE(wbuf[j] == v[wc]);
wc++;
}
}
}
}
}
Related
unique_ptr with custom allocator
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); } };
invalid initialization of reference of type '&' from expression of type '*'
I have a problem that is driving me crazy: I have a member function: template <class T, unsigned char _size> T& DynamicArray<T, _size>::append(const T& newE) { if((dedicated - (length + 1) == 0)) { transfer(length + 1); elements[length - 1] = newE; return elements[length - 1] = newE; } else return (elements[length++] = newE); } then, I call it this way: grphd::graphic::Entity& grphd::graphic::Grid::addEntity(graphic::Entity &e) { try { return entities.append(&e); //RVALUE = ENTITY* //LVALUE = ENTITY& ?! --> ENTITY*&! } catch(std::exception &e) { std::cout << "Exception on 'addEntity'.\n"; } } It gives me this error: error: invalid initialization of reference of type 'grphd::graphic::Entity&' from expression of type 'grphd::graphic::Entity*' EDIT: The error is at the "return entities.append(&e);" line. entities is DynamicArray<Entity*, 1> entities;. DynamicArray is: #ifndef DYN_ARR_H_INCLUDED #define DYN_ARR_H_INCLUDED template <class T, unsigned char _size> class DynamicArray { T* elements; //DONE unsigned char length; //DONE unsigned char dedicated; //DONE static unsigned char minDiffer; //DONE T& transfer(unsigned char); //DONE public: DynamicArray(); // DONE unsigned char getDedicated() const {return dedicated;} //DONE unsigned char getLength() const {return length;} //DONE void* getElementsPtr() const {return reinterpret_cast<void*>(elements);} //DONE T& append(const T&); //DONE T& insert(const T&, unsigned char); //DONE bool kickoutLast(); //DONE bool remove(unsigned char); //DONE T& operator[] (unsigned char); //DONE T operator[] (unsigned char) const; }; template <class T, unsigned char _size> void writeDynamicArray(const DynamicArray <T, _size>& da); #include "dyn_arr.cpp" #endif // DYN_ARR_H_INCLUDED --- and --- template <class T, unsigned char _size> DynamicArray<T, _size>::DynamicArray(): length(_size), dedicated(_size + minDiffer) { try { elements = new T[dedicated]; } catch(std::exception &e) { std::cout << "Error on allocation \"elements = new T[dedicated]\".\n"; } } template <class T, unsigned char _size> T& DynamicArray<T, _size>::transfer(unsigned char len) { T* tmpPtr; try { tmpPtr = new T[len + minDiffer]; } catch(std::exception &e) { std::cout << "Error on allocation \"T* tmpPtr = new T[len + minDiffer]\".\n"; } for( unsigned char i = 0; i < length; tmpPtr[i] = elements[i], i++ ); delete [] elements, elements = tmpPtr, length = len, dedicated = len + minDiffer; } template <class T, unsigned char _size> T& DynamicArray<T, _size>::append(const T& newE) { if((dedicated - (length + 1) == 0)) { transfer(length + 1); elements[length - 1] = newE; return elements[length - 1] = newE; } else return (elements[length++] = newE); } template <class T, unsigned char _size> bool DynamicArray<T, _size>::kickoutLast() { if((length - 1) != 0) { delete (elements + dedicated - 1); dedicated--; length--; return 1; } return 0; } template <class T, unsigned char _size> bool DynamicArray<T, _size>::remove(unsigned char n) { if(n >= (length - 1)) return false; kickoutLast(); for(unsigned char i = n; i < (length); i++) { elements[i] = elements[i + 1]; } return true; } template <class T, unsigned char _size> T& DynamicArray<T, _size>::insert(const T& newE, unsigned char n) { if((dedicated - (length + 1) == 0)) transfer(length + 1); for(short i = length; i > n; i--) { elements[i] = elements[i - 1]; } elements[n] = newE; length++; } template <class T, unsigned char _size> T& DynamicArray<T, _size>::operator[] (unsigned char n) { return elements[n]; } template <class T, unsigned char _size> T DynamicArray<T, _size>::operator[] (unsigned char n) const { return elements[n]; } template <class T, unsigned char _size> void writeDynamicArray(const DynamicArray <T, _size>& da) { for(unsigned char i = 0; i < da.getLength(); i++) std::cout << da[i] << '\n'; } template <class T, unsigned char _size> unsigned char DynamicArray<T, _size>::minDiffer = 8; Fixed with: grphd::graphic::Entity*& grphd::graphic::Grid::addEntity(graphic::Entity &e) { try { return entities.append(&e); //RVALUE = ENTITY* //LVALUE = ENTITY& ?! --> ENTITY*&! } catch(std::exception &e) { std::cout << "Exception on 'addEntity'.\n"; } } (The program crashes due to a memory access violation, btw).
Debug Assertion Failed! Expression: is_block_type_valid(header->_block_use). Object wont init and Push error
In advanced, let me thank your for looking at this code for me, because it has been bugging me for a while now and I can't seem to find the issue. Whenever I run it, it doesn't throw any errors within the console, instead it throws this error: Debug Assertion Failed! Program: [Filepath to .exe] File: minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp Line 892 Expression: is_block_type_valid(header->_block_use) I've been trying to figure it out, but it just wont work for me. I believe it has something to do with the template types I am passing in, as it only crashes whenever I try to initialize a minHeap object or try my Push method, which I also believe is an issue. Once again, thank you all so much for looking at my code. Main: #include "minHeap.h" #include "Node.h" #include <iostream> using namespace std; int main() { Node<char> A = Node<char>(0, 'A'); Node<char> B = Node<char>(1, 'B'); Node<char> C = Node<char>(2, 'C'); Node<char> D = Node<char>(3, 'D'); minHeap<char> myHeap = minHeap<char>(); //Below doesn't work, something about subscript is not of integrap type //myHeap.push(A.index, A.value); //myHeap.push(B.index, B.value); //myHeap.push(C.index, C.value); //myHeap.push(D.index, D.value); cout << A.index << endl; myHeap.~minHeap(); return 0; } Here is Node.h: #pragma once template<typename T> class Node { public: float index; T value; Node(float indx, T val); ~Node(); }; template<typename T> inline Node<T>::Node(float indx, T val) { index = indx; value = val; } template<typename T> inline Node<T>::~Node() { } And finally, minHeap: #pragma once template<typename T> class minHeap { private: T* arr[100]; int arrSize = 0; void heapifyUp(int indx); void heapifyDown(int indx); int getParent(int indx); int childLeft(int indx); int childRight(int indx); int swap(int indxA, int indxB); public: minHeap(); ~minHeap(); void push(int indx, T val); void pop(); }; template<typename T> inline minHeap<T>::minHeap() { } template<typename T> inline minHeap<T>::~minHeap() { delete[] arr; } template<typename T> inline void minHeap<T>::heapifyUp(int indx) { if (indx <= 0) return; int j = getParent(indx); if (arr[indx] < arr[j]) { int temp = arr[indx]; arr[indx] = arr[j]; arr[j] = temp; } heapifyUp(j); } template<typename T> inline void minHeap<T>::heapifyDown(int indx) { int j; //if no left child if (childLeft(indx) > arrSize - 1) return; //if no right child if (childRight(indx) > arrSize - 1) j = childLeft(indx); //No children else j = (arr[childLeft(indx)] < arr[childRight(indx)]) ? (childLeft(indx)):(childRight(indx)); if (arr[indx] > arr[indx]) { int temp = arr[indx]; arr[indx] = arr[j]; arr[j] = temp; } heapifyDown(j); } template<typename T> inline int minHeap<T>::getParent(int indx) { return (indx - 1) / 2; } template<typename T> inline int minHeap<T>::childLeft(int indx) { return 2 * i + 1; } template<typename T> inline int minHeap<T>::childRight(int indx) { return 2 * i + 2; } template<typename T> inline int minHeap<T>::swap(int indxA, int indxB) { int tempA = arr[indxA]; int tempB = arr[indxB]; arr[indxA] = tempB; arr[indxB] = tempA; return 0; } template<typename T> inline void minHeap<T>::push(int indx, T val) { //Something with Array is broken. Fix it pupper int tempVal = arr[indx]; arr[indx] = val; arrSize += 1; heapifyUp(arrSize - 1); } template<typename T> inline void minHeap<T>::pop() { int temp = arr[0]; arr[0] = arr[arrSize - 1]; arr[arrSize - 1] = nullptr; arrSize -= 1; heapifyDown(0); }
Why are you calling myHeap.~minHeap();? This results in myHeap being destroyed twice, with the second call trying to free memory that has already been freed. This can cause the error you're seeing. You can construct your variables a lot more concisely: Node<char> A(0, 'A'); minHeap<char> myHeap;
C++ Undefined reference to List<int>::GetCount() const [duplicate]
This question already has answers here: Why can templates only be implemented in the header file? (17 answers) Closed 9 years ago. I am writing a simple data structure library, while I met some problems. I wrote three files. collections.h is the header file, collections.cpp is to implement the methods declared in header file, main.cpp is for test. But compile error occurs: Undefined reference to List::GetCount() const; Undefined reference to List::IsEmpty() const; Undefined reference to List::Add(int); ...// And so on. I provide my code below, where is the problem? collections.h: #ifndef COLLECTIONS_H #define COLLECTIONS_H #include <windows.h> #include <stdexcept> template<typename T> class ISequenceList { protected: ISequenceList() { } public: virtual int GetCount() const = 0; virtual bool IsEmpty() const = 0; virtual void Add(T item) = 0; virtual void AddRange(const T *items, int length) = 0; virtual T &ElementAt(int index); virtual bool InsertAt(T item, int index); virtual bool Remove(T item) = 0; virtual bool RemoveAt(int index) = 0; virtual bool Contains(T item) = 0; }; template<typename T> class List : public ISequenceList<T> { private: int _count; int _capacity; T *_array; void ExpandCapacity(); public: List(int capacity = 100) { if (capacity <= 0) std::__throw_invalid_argument("The capcity can't be 0 or below."); this->_count = 0; this->_capacity = capacity; this->_array = (T*)malloc(_capacity* sizeof(T)); } List(const List &other) { if (this == other) return; this->_count = other->_count; this->_capacity = other->_capacity; free(_array); this->_array = other->_array; } List &operator=(const List &other) { this = other; } ~List() { if (_array) free(_array); } int GetCount() const; bool IsEmpty() const; T &ElementAt(int index); void Add(T item); void AddRange(const T *items, int length); bool InsertAt(T item, int index); bool Remove(T item); bool RemoveAt(int index); bool Contains(T item); }; #endif collections.cpp: #include "collections.h" template<typename T> void List<T>::ExpandCapacity() { T *temp = this->_array; this->_array = (T*)malloc((this->_capacity << 1) * sizeof(T)); memcpy(this->_array, temp, this->_capacity * sizeof(T)); this->_capacity = this->_capacity << 1; free(temp); } template<typename T> int List<T>::GetCount() const { return this->_count; } template<typename T> bool List<T>::IsEmpty() const { return this->_count == 0; } template<typename T> void List<T>::Add(T item) { this->_array[_count] = item; _count++; if (_count == _capacity) this->ExpandCapacity(); } template<typename T> void List<T>::AddRange(const T *items, int length) { if (length <= 0) std::__throw_invalid_argument("The length can't be 0 or below."); if (!items) std::__throw_invalid_argument("The items can't be null"); int totalLength = this->_count + length; if (totalLength >= this->_capacity) { T *temp = this->_array; this->_array = (T*)malloc((totalLength << 1) * sizeof(T)); memcpy(_array, temp, this->_capacity); free(temp); } this->_array += this->_capacity; memcpy(_array, items, length * sizeof(T)); this->_capacity = totalLength << 1; this->_count += length; } template<typename T> T &List<T>::ElementAt(int index) { if (index < 0 || index >= _count ) std::__throw_invalid_argument("The index is out of bound."); return _array[index]; } template<typename T> bool List<T>::InsertAt(T item, int index) { if (index < 0 || index > _count) return false; if (index == _count) { this->Add(item); return true; } for (int i = _count; i > index; i--) { _array[i] = _array[i - 1]; } _array[index] = item; _count++; if (_count == _capacity) this->ExpandCapacity(); return true; } template<typename T> bool List<T>::Remove(T item) { for (int i = 0; i < _count; i++) { if (_array[i] == item) { for (int j = i; j < _count; j++) { _array[j] = _array[j + 1]; } _count--; return true; } } return false; } template<typename T> bool List<T>::RemoveAt(int index) { if (index < 0 || index >= _count) return false; for (int j = index; j < _count; j++) { _array[j] = _array[j + 1]; } _count--; return true; } template<typename T> bool List<T>::Contains(T item) { for (int i = 0; i < _count; i++) { if (_array[i] == item) return true; } return false; } main.cpp: #include "collections.h" #include <iostream> int main() { List<int> *seqList = new List<int>(); seqList->Add(5); int arr[100] = {0}; seqList->AddRange(arr, 50); seqList->ElementAt(5) = 111; seqList->InsertAt(100, 15); seqList->Remove(50); seqList->ElementAt(44) = 44; seqList->RemoveAt(44); if (seqList->Contains(111)) std::cout << "Yes" << std::endl; for (int i = 0; i < seqList->GetCount(); i++) { std::cout << seqList->ElementAt(i) << "\t"; } return 0; } I have defined all the methods in List, but why can't the complier recognize? Where is the problem? Thanks for anyone who help me.. Note: my ide is Code::Blocks
Implementation of the template functions must be in a header; it can't be in a separate source file. The compiler needs to see it at the point where the template is used and its arguments become known.
protected member is not accessible by base class as expected
My derived class merge_sort from dynamic_array does not have access to protected member T* array. Their are error everywhere it is used saying such. I'm not sure why...except maybe the public designator for merge_sort should be something else? #include "c_include.cpp" using namespace std; template <class T> class dynamic_array { protected: T* array; public: int size; void rorder(); void order(); void randorder(); void print_operator(ostream&)const; dynamic_array(int sizein) { size=sizein; array=new T[size](); } }; template <class T> void dynamic_array<T>::print_operator(ostream &os=cout)const { for (int i = 0; i < size; i++) os << array[i] << endl; } template <class T> void dynamic_array<T>::randorder() { srand(time(NULL)); int *ap; for(ap=array;ap!=array+size;++ap){*ap=rand();} } template <class T> void dynamic_array<T>::order() { int *ap,i=0; for(ap=array;ap!=array+size;++ap) { *ap=i; ++i; } } template <class T> void dynamic_array<T>::rorder() { int *ap,i=size; for(ap=array;ap!=array+size;++ap) { *ap=i; --i; } } template<class T> ostream& operator<<(ostream& stream, dynamic_array<T> const& data) { data.print_operator(stream); return stream; } /* Merge Sort */ template <class T> class merge_sort : public dynamic_array <T> { private: const static int size; int scratch[]; void flip_if_unordered(int &x, int &y) { if(array[x]>array[y]) { int tmp=array[x]; array[x]=array[y]; array[y]=tmp; } } void merge_algo(int &left, int &right_begin, int &right) { int iter,iter_left=left,iter_right=right_begin; for(iter=left;iter<=right;++iter) { if( (iter_right>right) || ((iter_left < right_begin) && (array[iter_left]<=array[iter_right]))) { scratch[iter]=array[iter_left]; ++iter_left; } else { scratch[iter]=array[iter_right]; ++iter_right; } } for(iter=left;iter<=right;++iter){array[iter]=scratch[iter];} } void merge_recurse(int left,int right) { int left_end=(left+((right-left)/2)); int right_begin=left_end+1; if(((left+1)==right)){flip_if_unordered(left,right);return;} else if ((left==right)){return;} else { merge_recurse(left,left_end); merge_recurse(right_begin,right); merge_algo(left,right_begin,right); } } public: merge_sort() { scratch = new T[size](); if(scratch != NULL) { merge_recurse(0, size); } } }; /*Quick Sort void quick_sort() { quick_recurse(0,size); } void quick_recurse(int left, int right) { int l = left, r = right, tmp; int pivot = array[(left + right) / 2]; while (l <= r) { while (array[l] < pivot)l++; while (array[r] > pivot)r--; if (l <= r) { tmp = array[l]; array[l] = array[r]; array[r] = tmp; l++; r--; } } if (left < r)quick_recurse(left, r); if (l < right)quick_recurse(l, right); } */
Your base class depends on a template argument, so its type is a dependent type. The compiler won't know which specialization of the base class you use until is instantiated, so you have to help the compiler know that such identifier is a base's member. Either like this: dynamic_array<T>::array or this->array or using dynamic_array<T>::array;