Improving usage of C++ vector access - c++

I am writing currently an importer for Labplot to support BLF files. The importer works fine, but when looking at the performance it is visible that there is a lot of room to improve. It is visible that adding the data to the datacontainer consumes the most of the computational power. I tried already for testing using std::vector, but it has not that big impact.
I am not able to use a static array, because the actual number of messages is unknown (only upper limit is known), because if the dbcParser is not able to parse the message it will be skipped. So at the end of the import I have to resize the array.
Are there any recomandations how to improve the performance of the code?
Definition of v: QVector<const Vector::BLF::ObjectHeaderBase*> v;
bool firstMessageValid = false;
for (const auto ohb : v) {
int id;
std::vector<double> values;
if (ohb->objectType == Vector::BLF::ObjectType::CAN_MESSAGE) {
const auto message = reinterpret_cast<const Vector::BLF::CanMessage*>(ohb);
id = message->id;
m_dbcParser.parseMessage(message->id, message->data, values);
} else if (ohb->objectType == Vector::BLF::ObjectType::CAN_MESSAGE2) {
const auto message = reinterpret_cast<const Vector::BLF::CanMessage2*>(ohb);
id = message->id;
m_dbcParser.parseMessage(message->id, message->data, values);
} else
return 0;
if (values.size() == 0) {
// id is not available in the dbc file, so it is not possible to decode
DEBUG("Unable to decode message: " << id);
continue;
}
uint64_t timestamp;
timeInNS = getTime(ohb, timestamp);
if (convertTimeToSeconds) {
double timestamp_seconds;
if (timeInNS)
timestamp_seconds = (double)timestamp / pow(10, 9); // TimeOneNans
else
timestamp_seconds = (double)timestamp / pow(10, 5); // TimeTenMics
m_DataContainer.setData<double>(0, message_index, timestamp_seconds);
} else
m_DataContainer.setData<qint64>(0, message_index, timestamp);
if (firstMessageValid) {
const auto startIndex = idIndexTable.value(id) + 1; // +1 because of time
for (std::vector<double>::size_type i = 1; i < startIndex; i++) {
const auto prevValue = m_DataContainer.data<double>(i, message_index - 1);
m_DataContainer.setData<double>(i, message_index, prevValue);
}
for (std::vector<double>::size_type i = startIndex; i < startIndex + values.size(); i++) {
m_DataContainer.setData<double>(i, message_index, values.at(i - startIndex));
}
for (std::vector<double>::size_type i = startIndex + values.size(); i < m_DataContainer.size(); i++) {
const auto prevValue = m_DataContainer.data<double>(i, message_index - 1);
m_DataContainer.setData<double>(i, message_index, prevValue);
}
} else {
const auto startIndex = idIndexTable.value(id) + 1; // +1 because of time
for (std::vector<double>::size_type i = 1; i < startIndex; i++) {
m_DataContainer.setData<double>(i, message_index, 0);
}
for (std::vector<double>::size_type i = startIndex; i < startIndex + values.size(); i++) {
m_DataContainer.setData<double>(i, message_index, values.at(i - startIndex));
}
for (std::vector<double>::size_type i = startIndex + values.size(); i < m_DataContainer.size(); i++) {
m_DataContainer.setData<double>(i, message_index, 0);
}
firstMessageValid = true;
}
message_index++;
}
struct DataContainer {
void clear();
template<class T>
void appendVector(QVector<T>* data, AbstractColumn::ColumnMode cm) {
m_dataContainer.push_back(data);
m_columnModes.append(cm);
};
template<class T>
void setData(int indexDataContainer, int indexData, T value) {
auto* v = static_cast<QVector<T>*>(m_dataContainer.at(indexDataContainer));
v->operator[](indexData) = value;
}
template<class T>
T data(int indexDataContainer, int indexData) {
auto* v = static_cast<QVector<T>*>(m_dataContainer.at(indexDataContainer));
return v->at(indexData);
}
int size() const;
const QVector<AbstractColumn::ColumnMode> columnModes() const;
/*!
* \brief dataContainer
* Do not modify outside as long as DataContainer exists!
* \return
*/
std::vector<void*> dataContainer() const;
AbstractColumn::ColumnMode columnMode(int index) const;
const void* datas(int index) const;
bool resize(uint32_t) const;
private:
QVector<AbstractColumn::ColumnMode> m_columnModes;
std::vector<void*> m_dataContainer; // pointers to the actual data containers
};
Edit
Before the loop I resize every vector to the absolute maximum number of messages.
if (convertTimeToSeconds) {
auto* vector = new QVector<double>();
vector->resize(message_counter);
m_DataContainer.appendVector<double>(vector, AbstractColumn::ColumnMode::Double);
} else {
auto* vector = new QVector<qint64>();
vector->resize(message_counter);
m_DataContainer.appendVector<qint64>(vector, AbstractColumn::ColumnMode::BigInt); // BigInt is qint64 and not quint64!
}
for (int i = 0; i < vectorNames.length(); i++) {
auto* vector = new QVector<double>();
vector->resize(message_counter);
m_DataContainer.appendVector(vector, AbstractColumn::ColumnMode::Double);
}
During parsing I discard messages if I am not able to parse them, therefore message_index <= message_counter. So when having 100k messages, but I parse only 50k of them, I have to resize the array at the end to not waste memory.
m_DataContainer.resize(message_index);
Edit2
Replacing
auto* v = static_cast<QVector<T>*>(m_dataContainer.at(indexDataContainer));
v->operator[](indexData) = value;
by
static_cast<QVector<T>*>(m_dataContainer.at(indexDataContainer))->operator[](indexData) = value;
and replacing
auto* v = static_cast<QVector<T>*>(m_dataContainer.at(indexDataContainer));
return v->at(indexData);
by
return static_cast<QVector<T>*>(m_dataContainer.at(indexDataContainer))->at(indexData);
brought about 20%. I thought it will be optimized out at -O2 but was not.
With -O2 moving from QVector to std::vector was again an improvement of around 25%

Related

Still having an Unhandled Exception after editing it a bit

Inventory::Inventory()
{
this->cap = 10;
this->nrOfItems = 0;
this->itemArr = new Item* [cap]();
}
Inventory::~Inventory()
{
for (size_t i = 0; i < this->nrOfItems; i++)
{
delete this->itemArr[i];
}
delete[] this->itemArr;
}
void Inventory::expand()
{
this->cap *= 2;
Item **tempArr = new Item*[this->cap]();
for (size_t i = 0; i < this->nrOfItems, i++;)
{
tempArr[i] = new Item(*this->itemArr[i]);
}
for (size_t i = 0; i < this->nrOfItems, i++;)
{
delete this->itemArr[i];
}
delete[] this->itemArr;
this->itemArr = tempArr;
this->initialize(this->nrOfItems);
}
void Inventory::initialize(const int from)
{
for (size_t i = from; i < cap, i++;)
{
this->itemArr[i] = nullptr;
}
}
void Inventory::addItem(const Item& item)
{
if (this->nrOfItems >= this->cap)
{
expand();
}
this->itemArr[this->nrOfItems++] = new Item(item);
}
void Inventory::removeItem(int index)
{
}
Above is my code from Inventory.cpp and the issue is that I keep getting an Unhandled Exception from the line that has:
this->itemArr[i] = nullptr;
I have no idea where I'm messing up in the code. Below I am posting from Inventory.h:
class Inventory
{
private:
int cap;
int nrOfItems;
Item **itemArr;
void expand();
void initialize(const int from);
public:
Inventory();
~Inventory();
void addItem(const Item &item);
void removeItem(int index);
inline void debugPrint() const
{
for (size_t i = 0; i < this->nrOfItems; i++)
{
std::cout << this->itemArr[i]->debugPrint() << std::endl;
}
}
};
This is where itemArr should be housed but for some reason, it is not pulling. I am new to coding so I don't know all of the tips and tricks involved with it.
In the loop i < cap, i++;, you will first check if i is in bounds, and then increase it by one before the loop body executes. Thus if i=cap-1 the check will pass, i will become cap and you're out of bounds.
Instead write (size_t i = from; i < cap;i++), so that the increment of i is executed after the loop body.
A see several problems with your code:
In Inventory::expand(), since you are working with an array of pointers (why not an array of objects?), there is no need to make clones of the existing Item objects to the new array, just copy the existing pointers as-is. You are just expanding the array to fit more pointers, so the clones are wasted overhead. Not a fatal issue, but it is something you should be aware of.
In both Inventory::initialize() and Inventory::expand(), your loops are setup incorrectly. You are performing the i++ in the wrong place. Given this definition of a loop:
for ( <init-statement> <condition> ; <iteration_expression> )
You are performing the i++ as part of the <condition>, not the <iteration_expression>. A simply typo caused by you using the comma operator instead of ;, but an important typo nonetheless.
Inventory is violating the Rule of 3/5/0, by not implementing copy/move constructors and copy.move assignment operators.
With that said, try this instead:
class Inventory
{
private:
size_t cap;
size_t nrOfItems;
Item **itemArr;
void expand();
public:
Inventory();
Inventory(const Inventory &src);
Inventory(Inventory &&src);
~Inventory();
Inventory& operator=(Inventory rhs);
void addItem(const Item &item);
void removeItem(size_t index);
inline void debugPrint() const
{
for (size_t i = 0; i < nrOfItems; ++i)
{
std::cout << itemArr[i]->debugPrint() << std::endl;
}
}
};
Inventory::Inventory()
{
cap = 10;
nrOfItems = 0;
itemArr = new Item*[cap]();
}
Inventory::Inventory(const Inventory &src)
{
cap = src.cap;
nrOfItems = src.nrOfItems;
itemArr = new Item*[cap]();
for(size_t i = 0; i < nrOfItems; ++i)
{
itemArr[i] = new Item(*(src.itemArr[i]));
}
}
Inventory::Inventory(Inventory &&src)
{
cap = src.cap; src.cap = 0;
nrOfItems = src.nrOfItems; src.nrOfItems = 0;
itemArr = src.itemArr; src.itemArr = nullptr;
}
Inventory::~Inventory()
{
for (size_t i = 0; i < nrOfItems; ++i)
{
delete itemArr[i];
}
delete[] itemArr;
}
Inventory& Inventory::operator=(Inventory rhs)
{
Inventory temp(std::move(rhs));
std::swap(cap, temp.cap);
std::swap(nrOfItems, temp.nrOfItems);
std::swap(itemArr, temp.itemArr);
return *this;
}
void Inventory::expand()
{
size_t newCap = cap * 2;
Item **tempArr = new Item*[newCap]();
for (size_t i = 0; i < nrOfItems; ++i)
{
tempArr[i] = itemArr[i];
}
delete[] itemArr;
itemArr = tempArr;
cap = newCap;
}
void Inventory::addItem(const Item& item)
{
if (nrOfItems >= cap)
{
expand();
}
itemArr[nrOfItems] = new Item(item);
++nrOfItems;
}
void Inventory::removeItem(size_t index)
{
if (index < nrOfItems)
{
Item *item = itemArr[index];
for(size_t i = index + 1; i < nrOfItems; ++i)
{
itemArr[i-1] = itemArr[i];
}
--nrOfItems;
itemArr[nrOfItems] = nullptr;
delete item;
}
}
That being said, you really should be using std::vector instead, let it handle these details for you, eg:
#include <vector>
class Inventory
{
private:
std::vector<Item> itemVec;
public:
Inventory();
void addItem(const Item &item);
void removeItem(size_t index);
inline void debugPrint() const
{
for (Item &item : items)
{
std::cout << item.debugPrint() << std::endl;
}
}
};
Inventory::Inventory()
{
itemVec.reserve(10);
}
void Inventory::addItem(const Item& item)
{
itemVec.emplace_back(item);
}
void Inventory::removeItem(size_t index)
{
if (index < itemVec.size())
{
itemVec.erase(itemVec.begin() + index);
}
}
See how much simpler that is? :)

(FUZZING) Get a pointer to data range of dynamic array

EDIT: Clarification:
If I have an array int* a = new int[10], I want to get a pointer to a, but only the values from 0 to 5, without having to allocate another array for those values.
Original post:
I created a small class to fuzz my functions, but the thing is that it is painfully slow. It takes roughly 10-20 seconds to run my function 1000 times.
I decided to improve my code by allocating a very large array at first, then filling it from 0 to a randomly generated number and then just returning a pointer to that range to use in my function instead of allocating memory and deleting it each time.
Below is my code.
I attempt to allocate 1 million bytes at first, then I want to return a range from 0 to whatever size my class generated. Currently I allocate memory once more for returning it, but that's not efficient.
I use Xorshift to generate random numbers, which should be much faster than rand() so I think besides memory allocation it's pretty good, but any suggestions are very much welcome!
Note: if you do not understand part of my code ask me (it's written quickly, so it might be unintelligible at certain parts) ;)
class fuzz {
public:
fuzz() {
this->alloc_init_buff();
}
~fuzz() {
this->dealloc_init_buff();
}
int fill_buff(unsigned int size) {
if (size > this->m_buffsize) { size = this->m_buffsize; }
for (int i = 0; i < size; ++i) {
this->m_buff[i] = this->rand_xor();
}
return size;
}
int fill_buff() {
int size = this->rand_xor(1, this->m_buffsize);
if (size > this->m_buffsize) { size = this->m_buffsize; }
for (int i = 0; i < size; ++i) {
this->m_buff[i] = this->rand_xor();
}
return size;
}
unsigned char*& get_buff(int size) {
unsigned char* temp = new unsigned char[size];
memcpy((void*)temp, (void*)this->m_buff, size);
return temp;
}
private:
struct xr_xorshift_state {
unsigned int a = 123456789, b = 362436069, c = 521288629, d = 88675123;
};
unsigned int xorshift(xr_xorshift_state* state) {
unsigned int res = 0;
res = state->a ^ (state->a << 11);
state->a = state->b; state->b = state->c; state->c = state->d;
state->d = state->d ^ (state->d >> 19) ^ (res ^ (res >> 8));
res &= 0x7fffffff;
return res;
}
unsigned int rand_xor() {
return this->xorshift(&this->m_state);
}
unsigned int rand_xor(unsigned int min, unsigned int max) {
return (min + (this->rand_xor() % (max - min)));
}
void alloc_init_buff() {
this->m_buff = new unsigned char[this->m_buffsize];
}
void dealloc_init_buff() {
delete[] this->m_buff;
}
xr_xorshift_state m_state = { 0 };
unsigned char* m_buff = { 0 };
unsigned int m_buffsize = { 1000000 };
};
int find_newline(const char* text, int size) {
int pos = 0;
while (*text != '\n') {
if (pos == size) { return 0; }
++text; ++pos;
}
return pos;
}
int main() {
fuzz fz = {};
unsigned char* randdata = nullptr;
int lap = 0;
int th = 0;
for (;;) {
if (lap == 1000) {
lap = 0;
++th;
printf("%d thousand laps done!\n", th);
}
try {
int size = fz.fill_buff();
randdata = fz.get_buff(size);
const char* d = (const char*)randdata;
find_newline(d, size);
delete[] randdata;
++lap;
}
catch (...) {
printf("error!\n");
++lap;
}
}
getchar();
return 0;
}

Pointer to array of pointers to structs in C++

I need some help, I'm learing data structers and I got a task to write a programm based on array of pointers to structres which can add elements and do other task with array.I have next model of levels:
first level --> net of shops
second level --> shop
third level --> goods
I've written types for this
typedef struct
{
QString date;
QString prod_code;
QString name;
}goods;
typedef struct
{
QString address;
QString number;
void **sublevel;
}shop;
typedef struct
{
QString website;
QString name;
QString owner;
QString address;
void **sublevel;
}net;
Then I've created global variable void **Start which points to array of pointers:
// init list
void ** init_list()
{
void** p = new void*[SIZE_AR];
p = p+2;
((int*)p)[COUNT_POS] = 0;
((int*)p)[SIZE_POS] = SIZE_AR;
return p;
}
void ** Start = init_list();
COUNT_POS - index of elements where I store count of currently used elemnets
SIZE_POS - size of array allocated in dynamic memory
SIZE_AR - default size for array
But I get segmentation fault when I try to add to element to the last level
(for previous two ones works fine):
// expand array if it overfilled
void ExpandArrPtr (void **&ar, int &SizeAr, int Cnt)
{
void **arW;
arW = new void*[SizeAr+DELTA+2];
for (int K = SizeAr-1; K >= 0; K--) {
arW[K+2] = ar[K];
}
SizeAr = SizeAr + DELTA;
ar=ar-2;
delete []ar;
ar=arW+2;
((int*)ar)[COUNT_POS] = Cnt;
((int*)ar)[SIZE_POS] = SizeAr;
}
// binary search
void bin_search(void **start, QString key, int &pos, bool &find, Cmpmethod func)
{
int mid;
int high, low;
find = false;
if((int*)start[COUNT_POS] == 0)
{
pos = 0;
qDebug()<<"zero elem\n";
return;
}
low = 0;
high = ((int*)start)[COUNT_POS] - 1;
do
{
mid = (high + low) / 2;
int result = func(start[mid], key);
if(result == 0)
{
pos = mid;
find = true;
return;
}
else if(result == 1)
{
high = mid - 1;
}
else
{
low = mid + 1;
}
}while(low <= high);
pos = low;
}
// function for adding in any level
void addtosort(void **&start, void *pnew, int pos)
{
int count = ((int*)start)[COUNT_POS];
int size = ((int*)start)[SIZE_POS];
if(count == size)
{
ExpandArrPtr(start, size, count);
}
if(pos == count)
{
start[pos] = pnew;
}
else
{
for(int i = count;i >= pos;i--)
{
start[i+1] = start[i];
}
start[pos] = pnew;
}
count++;
((int*)start)[COUNT_POS] = count;
}
void add_goods(void **&Start, goods * Pnew)
{
int pos;
bool find;
bin_search((((shop*)(Start))->sublevel), Pnew->name, pos, find, compare_goods);
addtosort((((shop*)(Start))->sublevel), Pnew, pos);
}
// finding the item in second level to add
void find_place(QString key)
{
int pos;
bool find;
int count = ((int*)Start)[COUNT_POS];
for(int i = 0;i < count;i++)
{
bin_search(((net*)(Start)[i])->sublevel, key, pos, find, compare_shop);
if(find)
{
goods * Pnew = new goods;
Pnew->date = "foo"
Pnew->name = "bar"
add_goods(((net*)(Start)[pos])->sublevel, Pnew);
break;
}
}
}
What can cause such problem?

C++ Bubble sort dynamically allocated array

I wrote a bubble sorting algorithm which sorts a dynamically allocated array using string comparison.
Here is my code:
void AddressBook::bubble_sort_address_book(){
bool swapped = true;
while(swapped){
swapped = false;
for(int i = 0; i < noOfEmployees; i++){
if(employees[i].combined_name() > employees[i+1].combined_name()){
Employee temp_employee = employees[i+1];
employees[i+1] = employees[i];
employees[i] = temp_employee;
}
}
}
}
My problem is pretty obvious, yet I can not seem to figure out how to solve it: The code sometimes fails on the line (in an undefined manner) :
Employee temp_employee = employees[i+1]
Its pretty obvious because if i is equal to the end of the array, accessing memory with i+1 results in undefined behaviour. However, if I stop the for loop with noOfEmployees-1, this does not happen but the first element is never sorted (obviously).
How can I implement bubble sort properly? It seems as such a trivial task. Am I missing something?
The following simplified version in pure C works fine:
int employees[10]= {3,1,7,6,9,7,1,0,2,6};
int noOfEmployees= 10;
void bubble_sort_address_book(void){
bool swapped = true;
int i;
while(swapped){
swapped = false;
for(i = 0; i < noOfEmployees-1; i++){
if(employees[i] > employees[i+1]){
int temp_employee = employees[i+1];
employees[i+1] = employees[i];
employees[i] = temp_employee;
swapped= true;
}
}
}
}
int main()
{
int i;
bubble_sort_address_book();
for (i=0; i<noOfEmployees; i++) {
printf("emp %d= %d\n", i, employees[i]);
}
return 0;
}
As you request, the function of variable swapped is to indicate that following a complete pass through the array no swap occurred and so it indicates the array is now sorted.
You can use an explicit bound on the outer loop.
You should also split things out into smaller functions.
bool operator <(Employee const & lhs, Employee const & rhs) {
return lhs.combined_name() < rhs.combined_name();
}
// a.k.a. std::swap
void swap(Employee & lhs, Employee & rhs) {
Employee temp(static_cast<Employee&&>(lhs)); // a.k.a. std::move
lhs = static_cast<Employee&&>(rhs);
rhs = static_cast<Employee&&>(temp);
}
void bubble_sort_impl(Employee * begin, Employee * end) {
for (; end != begin; --end) {
for (Employee * it = begin; it+1 != end; ++it) {
if (*(it+1) < *it) {
swap(*it, *(it+1));
}
}
}
}
// do we really need "bubble_" or "_address_book" in this name?
void AddressBook::bubble_sort_address_book() {
bubble_sort_impl(employees, employees + noOfEmployees);
}
another solution:
#include <iostream>
#include <vector>
using namespace std;
int employees[10] = { 3,1,7,6,9,7,1,0,2,6 };
void bubble_sort_address_book(void) {
bool swapped = true;
int i;
int noOfEmployees = 10;
while (swapped) {
swapped = false;
for (i = 1; i <= noOfEmployees ; i++) {
if (employees[i] > employees[i - 1]) {
int temp_employee = employees[i - 1];
employees[i - 1] = employees[i];
employees[i] = temp_employee;
swapped = true;
}
}
}
}
int main()
{
int i;
int noOfEmployees = 10;
bubble_sort_address_book();
for (i = 0; i<noOfEmployees; i++) {
printf("emp %d= %d\n", i, employees[i]);
}
return 0;
}

Minimal C++ STL Vector Implementation Problems

I have a technical problem and it's really confusing me. I apologise in advance because I may not be giving the relevant details; I don't yet why it's going wrong and it would be excessive to include all the code I'm working with.
I'm working with a large program that uses the C++ STL. I'm moving this code to a very sensitive environment without a standard clib nor STL implementaton; it will redefine malloc/free/new/delete etc... For that, I need to replace the std:: parts with my own simplified implementations. I've started with std::vector. Right now it's running in the standard ecosystem so it's the GNU libc and STL. The only thing that's changed is this vector class.
When I execute the program with the replaced class, it segfaults. I've put this through GDB and found that the program will request an object from the vector using the subscript operator. When the object reference is returned, a method is invoked and the program segfaults. It seems it can't find this method and ends up in main_arena() in GDB. The type of the object is an inherited class.
I'm really not sure at all what the problem is here. I would love to provide additional details, but I'm not sure what more I can give. I can only assume something is wrong with my vector implementation because nothing else in the program has been changed. Maybe there's something obvious that I'm doing wrong here that I'm not seeing at all.
I'm using: g++ (GCC) 4.4.5 20110214 (Red Hat 4.4.5-6)
I'd really appreciate any feedback/advice!
#ifndef _MYSTL_VECTOR_H_
#define _MYSTL_VECTOR_H_
#include <stdlib.h>
#include <assert.h>
typedef unsigned int uint;
namespace mystl
{
/******************
VECTOR
********************/
template <typename T>
class vector
{
private:
uint _size;
uint _reserved;
T *storage;
void init_vector(uint reserve)
{
if (reserve == 0)
{
_reserved = 0;
return;
}
storage = (T*)malloc(sizeof(T)*reserve);
assert(storage);
_reserved = reserve;
}
public:
vector()
{
// std::cerr << "default constructor " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
}
vector(const vector<T> &other)
{
// std::cerr << "copy constructor " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
init_vector(other.size());
_size = other.size();
for (uint i=0; i<other.size(); i++)
{
storage[i] = T(other[i]);
}
}
vector(uint init_num, const T& init_value)
{
// std::cerr << "special constructor1 " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
init_vector(init_num);
for (size_t i=0; i<init_num; i++)
{
push_back(init_value);
}
}
vector(uint init_num)
{
// std::cerr << "special constructor2 " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
init_vector(init_num);
}
void reserve(uint new_size)
{
if (new_size > _reserved)
{
storage = (T*)realloc(storage, sizeof(T)*new_size);
assert(storage);
_reserved = new_size;
}
}
void push_back(const T &item)
{
if (_size >= _reserved)
{
if (_reserved == 0) _reserved=1;
reserve(_reserved*2);
}
storage[_size] = T(item);
_size++;
}
uint size() const
{
return _size;
}
~vector()
{
if (_reserved)
{
free(storage);
storage = NULL;
_reserved = 0;
_size = 0;
}
}
// this is for read only
const T& operator[] (unsigned i) const
{
// do bounds check...
if (i >= _size || i < 0)
{
assert(false);
}
return storage[i];
}
T& operator[] (unsigned i)
{
// do bounds check...
if (i >= _size || i < 0)
{
assert(false);
}
return storage[i];
}
// overload = operator
const vector<T>& operator= (const vector<T>& x)
{
// check for self
if (this != &x)
{
_reserved = 0;
_size = 0;
storage = NULL;
init_vector( x.size() );
for(uint i=0; i<x.size(); i++)
{
storage[i] = T(x[i]);
}
_size = x.size();
}
return *this;
}
uint begin() const
{
return 0;
}
void insert(uint pos, const T& value)
{
push_back(value);
if (size() == 1)
{
return;
}
for (size_t i=size()-2; i>=pos&& i>=0 ; i--)
{
storage[i+1] = storage[i];
}
storage[pos] = value;
}
void erase(uint erase_index)
{
if (erase_index >= _size)
{
return;
}
//scoot everyone down by one
for (uint i=erase_index; i<_size; i++)
{
storage[i] = storage[i+1];
}
_size--;
}
void erase(uint start, uint end)
{
if (start > end)
{
assert(false);
}
if (end > _size)
end = _size;
for (uint i=start; i<end; i++)
{
erase(start);
}
assert(false);
}
void clear()
{
erase(0,_size);
}
bool empty() const
{
return _size == 0;
}
}; //class vector
}
#endif // _MYSTL_VECTOR_H_
Wow!
Your assignment operator also leaks memory.
Becuause you are using malloc/release the constructor to your type T will will not be called and thus you can not use your vector for anything except the most trivial of objects.
Edit:
I am bit bored this morning: Try this
#include <stdlib.h> // For NULL
#include <new> // Because you need placement new
// Because you are avoiding std::
// An implementation of swap
template<typename T>
void swap(T& lhs,T& rhs)
{
T tmp = lhs;
lhs = rhs;
rhs = tmp;
}
template <typename T>
class vector
{
private:
unsigned int dataSize;
unsigned int reserved;
T* data;
public:
~vector()
{
for(unsigned int loop = 0; loop < dataSize; ++loop)
{
// Because we use placement new we must explicitly destroy all members.
data[loop].~T();
}
free(data);
}
vector()
: dataSize(0)
, reserved(10)
, data(NULL)
{
reserve(reserved);
}
vector(const vector<T> &other)
: dataSize(0)
, reserved(other.dataSize)
, data(NULL)
{
reserve(reserved);
dataSize = reserved;
for(unsigned int loop;loop < dataSize;++loop)
{
// Because we are using malloc/free
// We need to use placement new to add items to the data
// This way they are constructed in place
new (&data[loop]) T(other.data[loop]);
}
}
vector(unsigned int init_num)
: dataSize(0)
, reserved(init_num)
, data(NULL)
{
reserve(reserved);
dataSize = reserved;
for(unsigned int loop;loop < dataSize;++loop)
{
// See above
new (&data[loop]) T();
}
}
const vector<T>& operator= (vector<T> x)
{
// use copy and swap idiom.
// Note the pass by value to initiate the copy
swap(dataSize, x.dataSize);
swap(reserved, x.rserved);
swap(data, x.data);
return *this;
}
void reserve(unsigned int new_size)
{
if (new_size < reserved)
{ return;
}
T* newData = (T*)malloc(sizeof(T) * new_size);
if (!newData)
{ throw int(2);
}
for(unsigned int loop = 0; loop < dataSize; ++loop)
{
// Use placement new to copy the data
new (&newData[loop]) T(data[loop]);
}
swap(data, newData);
reserved = new_size;
for(unsigned int loop = 0; loop < dataSize; ++loop)
{
// Call the destructor on old data before freeing the container.
// Remember we just did a swap.
newData[loop].~T();
}
free(newData);
}
void push_back(const T &item)
{
if (dataSize == reserved)
{
reserve(reserved * 2);
}
// Place the item in the container
new (&data[dataSize++]) T(item);
}
unsigned int size() const {return dataSize;}
bool empty() const {return dataSize == 0;}
// Operator[] should NOT check the value of i
// Add a method called at() that does check i
const T& operator[] (unsigned i) const {return data[i];}
T& operator[] (unsigned i) {return data[i];}
void insert(unsigned int pos, const T& value)
{
if (pos >= dataSize) { throw int(1);}
if (dataSize == reserved)
{
reserve(reserved * 2);
}
// Move the last item (which needs to be constructed correctly)
if (dataSize != 0)
{
new (&data[dataSize]) T(data[dataSize-1]);
}
for(unsigned int loop = dataSize - 1; loop > pos; --loop)
{
data[loop] = data[loop-1];
}
++dataSize;
// All items have been moved up.
// Put value in its place
data[pos] = value;
}
void clear() { erase(0, dataSize);}
void erase(unsigned int erase_index) { erase(erase_index,erase_index+1);}
void erase(unsigned int start, unsigned int end) /* end NOT inclusive so => [start, end) */
{
if (end > dataSize)
{ end = dataSize;
}
if (start > end)
{ start = end;
}
unsigned int dst = start;
unsigned int src = end;
for(;(src < dataSize) && (dst < end);++dst, ++src)
{
// Move Elements down;
data[dst] = data[src];
}
unsigned int count = start - end;
for(;count != 0; --count)
{
// Remove old Elements
--dataSize;
// Remember we need to manually call the destructor
data[dataSize].~T();
}
}
unsigned int begin() const {return 0;}
}; //class vector
With your current memory handling, this vector would only work with plain old data types.
To handle all types, it must ensure that objects
are actually created (malloc doesn't do that),
destroyed (free doesn't do that),
and you can't reallocate memory with realloc, because complex objects are not guaranteed to remain valid if they are byte-wise copied to another location.
Looks like the answer can be found in your question: "When the object reference is returned, a method is invoked and the program segfaults. It seems it can't find this method and ends up in main_arena() in GDB. The type of the object is an inherited class."
You probably store base class instance T in the vector, but make push_back for the instance of the class inherited from T. In push_back {storage[_size] = T(item);} you cast (actually make copy constructor T:T(const T&)) item to T (this probably named 'type cut'), then get reference to T and invoke a method of the class inherited from T using virtual table of T where the method is not defined yet/abstract. Am I right?
To make it properly work you should put T* in the vector or shared_ptr/unique_ptr depending on the ownership terms you apply to vector elements.
Generally in vector you can store only POD (Plain Old Data) types.