I am attempting to create a container class for a std::vector, to teach myself a bit more about templates, overloading operators, and managing exceptions.
For the moment, I'm just defining the basic operations. I have a template class listed below; I've overloaded the += and [] operators to push_back the vector with a T and access the elements of the vector directly, respectively. This works as expected.
The += operator does what it's supposed to do, and attempting to use the [] operator on an element out of range will throw the exception as intended.
Here is the prototype class and implementation as it currently stands:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
template <class T>
class Inventory
{
public:
void operator += (const T& b) { backpack.push_back(b); }
T operator [] (const unsigned& b)
{
if (backpack.empty() || backpack.size() < b)
throw string("Out of Range");
return backpack[b];
}
void operator -= (const unsigned& b)
{
if (backpack.empty() || backpack.size() < b)
throw string("No such element exists.");
backpack.erase(backpack.begin() + b);
}
private:
vector<int> backpack;
};
int main()
{
Inventory<int> pack;
pack += 2;
pack += 4;
try
{
cout << "It was " << pack[0] << endl;
cout << "It was " << pack[1] << endl;
pack -= 0;
cout << "It is now " << pack[0] << endl;
//pack -= 1; // Segfaults?
}
catch (string e)
{
cout << "Error: " << e << endl;
}
}
The issue is with the -= operator, intended to erase an element at the indicated position on the right hand side. When I stay within the boundaries of the vector, this works as intended; however, I do not get an exception if I specify an out of bounds number to erase; I get a seg-fault instead. I have attempted to determine the exact point the segfault occurs by adding additional print commands:
void operator -= (const unsigned& b)
{
cout << "In Overload!\n";
if (backpack.empty() || backpack.size() < b)
{
cout << "Exception!\n";
throw string("No such element exists.");
}
backpack.erase(backpack.begin() + b);
}
The "Exception!" line is never reached. The program faults before it can reach that point, even though I should be evaluating for undefined behavior. I believe I'm missing a key component in understanding how this process works. Is there a way I should be writing this so it can throw instead of fault?
Compiling using g++ -std=c++17 -Wall -Wextra -pedantic on Linux x64 architecture.
Your error checking is off by 1.
if (backpack.empty() || backpack.size() < b)
If the std::vector backpack contains only two values, backpack.size() is going to be 2, and backpack will contain backpack[0] and backpack[1].
Unfortunately, if the index b gets passed in as 2, this code will still attempt to access backpack[2], resulting in undefined behavior.
In fact, the entire if statement can be simply rewritten as:
if (b >= backpack.size())
throw string("Out of Range");
You have an "off by one" error in your code.,
Consider what happens if the array is not empty and b == backpack.size() in the code.
if (backpack.empty() || backpack.size() < b)
throw string("Out of Range");
return backpack[b];
In this case, valid indices for elements of backpack are 0 through to backpack.size() - 1.
If b == backpack.size(), the code will NOT throw an exception, and WILL attempt to return backpack[backpack.size()] which gives undefined behaviour.
One possible symptom of undefined behaviour is a "segfault".
One way to avoid the problem is to change the test to backpack.size() <= b.
Another alternative is to take advantage of std::vector::at() which will throw a std::out_of_range exception on an out-of-bounds index:
T operator [] (const unsigned& b)
{
try
{
return backpack.at(b);
}
catch (std::out_of_range& e)
{
throw string("Out of Range");
}
}
void operator -= (const unsigned& b)
{
try
{
backpack.at(b);
backpack.erase(backpack.begin() + b);
}
catch(std::out_of_range& e)
{
throw std::string("No such element exists.");
}
}
Live Example
Related
I am trying to figure out a way for qsort to throw an exception or indicate an error condition if the compare function finds that the elements are, for some reason, invalid for sorting.
For example, in this compare function, I need to indicate an error condition that my sorting is invalid if the return value of some_function is 5.
How should I modify my compare function?
int compare (const void * a, const void * b)
{
int ret1 = some_func(a);
int ret2 = some_func(b):
return ( ret1 - ret2 );
}
I am dealing with a legacy code base so I'm not in the position to use std::sort and due to the nature of the implementation calling some_func before hand might also involve huge amount of changes so I'm looking to understand if a workaround is possible.
C++ allows you to throw whatever you need, not only exceptions but also other types, you could do something like throw an int if it suits your purposes and catch where you call the function with a try-catch block.
For what you need I think you can use STL exception library:
Demostrative example:
#include <iostream>
#include <exception>
int count = 0;
int compare(const void *a, const void *b)
{
int ret1 = *(int*)a > *(int*)b;
if (++count == 5) //throws exception when count reaches 5
throw std::invalid_argument("Argument is not sortable");
//you could throw count by simply using throw count
return ret1;
}
int main()
{
int x[]{2,1,3,5,6,1,7,2,5,3};
try
{
//will sort until exception is thrown
qsort(x, sizeof x / sizeof *x, sizeof(int), compare);
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl; //print exception in the console
//handle the exception here
//if you were to throw count you could cach it with
//catch (int &e)
}
//note that the values were sorted until the exception was thrown
for (int i = 0; i < sizeof x / sizeof *x; i++){
std::cout << x[i] << " ";
}
}
Output:
Argument is not sortable
1 2 3 5 6 1 7 2 5 3
^
sorting
stopped
here
Throwing an exception is potentially expensive, so you probably want to return an error condition. However, doing either approach inside the compare function is needlessly expensive in this case, since you would be doing the check multiple times for every element. Instead, you could just check for the error condition before calling qsort which is much more efficient:
auto ok = std::none_of(/* range */, /* predicate */);
if (ok)
std::qsort(/* ... */)
else
// report error
I am having trouble returning the value of an out of bounds array using an exception formalism.
I create an array object in my main script with for example length 10 using a class Array. Than I try to set the 11th element of the array to a certain value. In my class Array I throw back a value from a class ArrayOutOfBounds when the index of an array is larger than the size of the array, which is true in this case. In my main script I than catch this object from the ArrayOutofBounds class to give the out of bounds index and a terminal message that states that the array is out of bounds. However, how do I return the actual value I was trying to set the 11th element to?
It looks as follows:
class ArrayOutOfBounds + overloading operator << function
class ArrayOutOfBounds {
public:
ArrayOutOfBounds(int index) : _index(index) {} ;
int index() const { return _index ; }
private:
int _index ;
} ;
ostream& operator<<(ostream& os,const ArrayOutOfBounds& m) {
return (os << "Array index is out of bounds") ;
}
Throw back function within class Array:
double& operator[](int index) {
if(index > _size) {
ArrayOutOfBounds aoob(index) ;
throw aoob ;
}
return _arr[index] ;
Main script:
int main() {
Array array(10) ;
try {
array[11] = 10 ;
}
catch(ArrayOutOfBounds aoob) {
cout << '\n' ;
cout << aoob << " (index, value): " << aoob.index() << ", " << 10 << endl ;
}
cout << '\n' ;
return 0 ;
}
I tried to add a second throw value that throws _arr[index] but I understood you can't throw two things at once.
However, how do I return the actual value I was trying to set the 11th element to?
You are not setting anything with the function. Since the function returns a reference your class has no idea what the caller is doing with it. It could be setting a value, making a copy, passing it to a function, it just can't know. If you need to know then you need to rewrite the operator to overload the function operator () and then you can take in the index and the value you want to set at the index.
You can then modify the ArrayOutOfBounds to take in two parameters. The index and the value and then you can access them inside the catch block.
It is not possible with the operator[] as it is currently written because the exception is thrown before there is any attempt to assign a value to the reference.
Instead of using operator[] you could write a different member function to set values that takes two parameters, an index and a value:
void setAt(int index, double value) {
if (index < 0 || index >= values.size())
throw ArrayOutOfBounds{index, value};
values[index] = value;
}
Live demo.
You could change operator[] so that it returns a proxy object that can be in an "out-of-bounds" state and then only when you try and assign a value to that proxy object is the exception thrown:
ArrayElementProxy operator[](int index) {
return ArrayElementProxy{*this, index};
}
class ArrayElementProxy {
Array* array_;
int index_;
public:
ArrayElementProxy(Array& array, int index) : array_(&array), index_(index){}
Array& operator=(double value){
array_->setAt(index_, value);
return *array_;
}
operator double() const{
return array_->getAt(index_);
}
};
Live demo.
But I think proxy objects are generally best avoided (just ask anyone who has tried to use vector<bool>).
I am taking a c++ class and one of the tasks is the following:
they gave us a main program and we had to write the code behind it. Here is that code:
vector<int> iv(4);
iv[3]=42;
cout << iv[3] << endl; // prints "42"
try {
cout << iv[1] << endl; // throw exception
}
catch (vector<int>::Uninitialized&){
cout << "Uninitialized vector element!" << endl;
}
return 0;
I've come up with this code for the vector template:
T* data;
unsigned int size;
public:
class Uninitialized{};
explicit vector(int size) {
data = new T[size];
this -> size = size;
for (unsigned i = 0; i < size; i++) {
data[i] = 0;
}
}
~vector() {
delete [] data;
}
T& operator[](int index) {
if (index >= size) {
abort();
}
return data[index];
}
friend ostream& operator<< (ostream& o, const T& t) {
if (t == 0)
throw Uninitialized();
else
return o << t;
}
However, the friend method is never called, so the exception is never thrown.
You're throwing at
iv[3]=42;
because at that point, data[3] == 0.
Remember, even in your assignment you're calling operator[]; it doesn't care (or even know) if you want the T& for reading or writing.
You should approach this kind of problem (and, in general, problems where the code is simple enough and you know how to make the bug appear) by stepping through it with a debugger first. You would have caught this -- you'd immediately see that the exception isn't being thrown where you think it is.
When your code runs iv[3] = 0, which is outside the try .. catch, it will throw an exception because iv[3] is zero at that point.
You need to tell when an assignment has happened to an element. I suspect your best bet is to return a proxy class from operator [], which then has operators for assignment and converstion to T.
This is a class template for an Array. I overloaded the [ ] operator in hopes it would fix the "out of bounds" issue. The print outs work well, except if it falls out of range, the compiler enables the range by default and it displays a 6 digit number.
Perhaps looking for a better way to initialize the arrays with the appropriate element number for a better check and if it does fall out of range when looking up the element, display an error.
// implement the class myArray that solves the array index
// "out of bounds" problem.
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
template <class T>
class myArray
{
private:
T* array;
int begin;
int end;
int size;
public:
myArray(int);
myArray(int, int);
~myArray() { };
void printResults();
// attempting to overload the [ ] operator to find correct elements.
int operator[] (int position)
{if (position < 0)
return array[position + abs(begin)];
else
return array[position - begin];
}
};
template <class T>
myArray<T>::myArray(int newSize)
{
size = newSize;
end = newSize-1;
begin = 0;
array = new T[size] {0};
}
template <class T>
myArray<T>::myArray(int newBegin, int newEnd)
{
begin = newBegin;
end = newEnd;
size = ((end - begin)+1);
array = new T[size] {0};
}
// used for checking purposes.
template <class T>
void myArray<T>::printResults()
{
cout << "Your Array is " << size << " elements long" << endl;
cout << "It begins at element " << begin << ", and ends at element " << end << endl;
cout << endl;
}
int main()
{
int begin;
int end;
myArray<int> list(5);
myArray<int> myList(2, 13);
myArray<int> yourList(-5, 9);
list.printResults();
myList.printResults();
yourList.printResults();
cout << list[0] << endl;
cout << myList[2] << endl;
cout << yourList[9] << endl;
return 0;
}
First of all, your operator[] is not correct. It is defined to always return int. You will get compile-time error as soon as you instantiate array of something, that is not implicitly convertible to int.
It should rather be:
T& operator[] (int position)
{
//...
}
and, of course:
const T& operator[] (int position) const
{
//you may want to also access arrays declared as const, don't you?
}
Now:
I overloaded the [ ] operator in hopes it would fix the "out of bounds" issue.
You didn't fix anything. You only allowed clients of your array to define custom boundaries, nothing more. Consider:
myArray<int> yourList(-5, 9);
yourList[88] = 0;
Does your code check for out-of-bounds cases like this one? No.
You should do it:
int operator[] (int position)
{
if((position < begin) || (position > end)) //invalid position
throw std::out_of_range("Invalid position!");
//Ok, now safely return desired element
}
Note, that throwing exception is usually the best solution in such case. Quote from std::out_of_range doc:
It is a standard exception that can be thrown by programs. Some components of the standard library, such as vector, deque, string and bitset also throw exceptions of this type to signal arguments out of range.
An better option to redefining an array class is to use the containers from the std library. Vector and array(supported by c++11). They both have an overloaded operator [] so you can access the data. But adding elements using the push_back(for vector) method and using the at method to access them eliminates the chance or getting out of range errors, because the at method performs a check and push_back resizes the vector if needed.
I've read through stack overflow threads multiple times in the past, and they're often quite helpful. However, I've run into a problem that simply doesn't make sense to me, and I'm trying to figure out what I missed. Here's the sections of the code that I'm having trouble with:
class BigInts
{
public:
static const std::size_t MAXLEN = 100;
BigInts(signed int i); //constructor
BigInts(std::string &); //other constructor
std::size_t size() const;
digit_type operator[](std::size_t ) const;
private:
digit_type _data[MAXLEN];
bool _negative;
int _significant;
};
//nonmember functions
std::ostream & operator << (std::ostream &, const BigInts &);
BigInts::BigInts(signed int i)
{
_negative = (i < 0);
if (i < 0)
{
i = -1*i;
}
std::fill(_data, _data+MAXLEN, 0);
if (i != 0)
{
int d(0);
int c(0);
do
{
_data[d++] = ( i % 10);
i = i / 10;
c++; //digit counter
}while(i > 0);
//_significant = c; //The problem line
assert(c <= MAXLEN); //checks if int got too big
}
}
std::size_t BigInts::size() const
{
std::size_t pos(MAXLEN-1);
while (pos > 0 && _data[pos] == 0)
--pos;
return pos+1;
}
std::ostream & operator << (std::ostream & os, const BigInts & b)
{
for (int i = (b.size() - 1); i >= 0; --i)
os << b[i];
return os;
}
int main()
{
signed int a, b;
std::cout << "enter first number" << std::endl;
std::cin >> a;
std::cout << "enter second number" << std::endl;
std::cin >> b;
BigInts d(a), e(b), f(b);
std::cout << d << " " << e << " " << f;
Major edit, switched from an attempted dummy version of the code to the actual code I'm using, complete with the original variable names. I tried to remove anything that isn't relevant to the code I'm currently working with, but if you see a strange name or call in there, let me know and I can post the associated portion.
The code had been working fine prior to the introduction of _significant, which is a variable I had added to add some more functionality to the class as a whole. However, when I attempted to drive the basic parts of it using the main function you see displayed, it encountered large errors. For example, I inputted 200 and 100 for a and b respectively, it outputted 201, 1, and 3 for d, e, and f. As it currently stands, the ONLY place _significant appears is when I'm attempting to assign the value of c to it.
The only error I can see right now is that _significant isn't initialized when the input is zero.
Step through it in a debugger, make sure the the right digits are ending up in the array and that the array data isn't being overwritten unexpectedly.
EDIT: It works for me (cleaned up slightly). More cleaned up, also working: http://ideone.com/MDQF8
If your class is busted purely by assigning to a member variable, that means stack corruption without a doubt. Whilst I can't see the source offhand, you should replace all buffers with self-length-checking classes to verify accesses.
The line i - 1; in the original code looks highly suspicious. Did you want to write i -= 1; or --i; or something else?
It decrements i by 1 and then throws away the result.