I’ve got this here class defined in a header file:
class E_IndexList {
public:
E_UIntegerList* l;
inline void *data() { // retrieve packed data: stride depends on type (range)
return l->data();
}
inline void insert(unsigned value) {
if (value > maxval[l->range]) {
promote();
insert(value);
} else {
l->push_back(value);
}
}
inline size_t size() {
return l->size();
}
inline unsigned long get(int index) {
return l->get(index);
}
void promote() {
if (l->range == E_UIntegerList::e_byte) {
E_UShortList *new_short_list = new E_UShortList(*((E_UByteList*)l));
delete l;
l = new_short_list;
} else if (l->range == E_UIntegerList::e_short) {
E_UIntList *new_int_list = new E_UIntList(*((E_UShortList*)l));
delete l;
l = new_int_list;
} else ASSERT(false);
}
// start off with bytes by default
E_IndexList() {
l = new E_UByteList;
}
E_IndexList(E_UIntegerList::int_bits range) {
switch(range) {
case E_UIntegerList::e_byte:
l = new E_UByteList;
break;
case E_UIntegerList::e_short:
l = new E_UShortList;
break;
case E_UIntegerList::e_int:
l = new E_UIntList;
break;
default:
ASSERT(false);
break;
}
}
E_IndexList(const E_IndexList& cpy) { // copy ctor
switch(cpy.l->range) {
case E_UIntegerList::e_byte:
l = new E_UByteList(((E_UByteList*)cpy.l)->list);
break;
case E_UIntegerList::e_short:
l = new E_UShortList(((E_UShortList*)cpy.l)->list);
break;
case E_UIntegerList::e_int:
l = new E_UIntList(((E_UShortList*)cpy.l)->list);
break;
default:
ASSERT(false);
break;
}
}
~E_IndexList() {
delete l;
}
};
Here are some more classes it makes use of:
static const unsigned long maxval[] = {0xff,0xffff,0xffffffff};
class E_UIntegerList {
public:
enum int_bits {e_byte = 0, e_short = 1, e_int = 2};
virtual ~E_UIntegerList() {}
int_bits range;
virtual void push_back(int i) = 0;
virtual void *data() = 0;
virtual size_t size() = 0;
virtual unsigned long get(int index) = 0;
};
struct E_UByteList:public E_UIntegerList {
std::vector<unsigned char> list;
E_UByteList() {
range = e_byte;
}
E_UByteList(const std::vector<unsigned char>& copy) {
list = copy;
}
inline void push_back(int i) {
list.push_back(i);
}
inline void *data() { return list.data(); }
inline size_t size() { return list.size(); }
inline unsigned long get(int index) { return list[index]; }
};
struct E_UShortList:public E_UIntegerList {
std::vector<unsigned short> list;
E_UShortList() {
range = e_short;
}
E_UShortList(const std::vector<unsigned short>& copy) {
list = copy;
}
E_UShortList(const E_UByteList& promotee) {
range = e_short;
list.assign(promotee.list.begin(),promotee.list.end()); // assignment should be compatible
}
inline void push_back(int i) {
list.push_back(i);
}
inline void *data() { return list.data(); }
inline size_t size() { return list.size(); }
inline unsigned long get(int index) { return list[index]; }
};
struct E_UIntList:public E_UIntegerList {
std::vector<unsigned int> list;
E_UIntList() {
range = e_int;
}
E_UIntList(const std::vector<unsigned int>& copy) {
list = copy;
}
E_UIntList(const E_UShortList& promotee) {
range = e_int;
list.assign(promotee.list.begin(),promotee.list.end());
}
inline void push_back(int i) {
list.push_back(i);
}
inline void *data() { return list.data(); }
inline size_t size() { return list.size(); }
inline unsigned long get(int index) { return list[index]; }
};
Now the way that I use this class is I have a std::vector<E_IndexList> that I use as a container of index lists.
The strange behavior is that when I run the program sometimes it has no problems and sometimes it asserts false.
So this is a big red flag for me because something super fishy is going on. I will very likely end up abandoning the entire E_IndexList until I start working on game netcode which is a long ways off. But, I’d like to know what’s going on here.
Every ctor I have sets the range to a valid value out of the enum in E_UIntegerList, so how could that assertion ever get tripped? And I can’t begin to come up with an explanation of why the behavior is inconsistent. The test that calls this code is not multi-threaded.
You didn’t define an assignment operator. See rule of three.