Why is my out_of_range exception not being caught - c++

I am new to cpp and I am trying out several things. This one I can't seem to figure out on my own.
#include <cstdio>
#include <stdexcept>
template <class E, class V>
struct Pair {
E first;
V second;
Pair(E fst, V snd) : first(fst), second(snd) {}
E getFirst() { return first; }
V getSecond() { return second; }
};
template <class t, unsigned dim>
struct vec {
t d[dim];
static constexpr int dimen = dim;
t &operator[](unsigned n) {
std::printf("dim: %d %d\n", dim, n);
if (n >= dim) {
std::printf("checking %d\n", n);
throw std::out_of_range("vector index is out of range");
}
return d[n];
};
};
int main() {
try {
Pair<int, vec<int, 2> *> test2(2, new vec<int, 2>{1, 2});
std::printf("%d\n", test2.getSecond()->dimen);
std::printf("before\n");
std::printf("%d\n", test2.getSecond()->d[2]); // it seems like the compiler kind of ignores this
} catch (std::out_of_range e) {
std::printf("Caught!!");
}
return 0;
}
Now, the line std::printf("%d\n", test2.getSecond()->d[2]); should ideally throw the out_of_range error, but it is not. My linter actually warns me that this is out of range also. I can compile and run the program and it returns some garbage 0 value.
My question is: why is either the error not being thrown or the error not being caught? I think the error is not being thrown because checking is not printed when I run it.

Because the throw code is never actually reached.
In this line here:
std::printf("%d\n", test2.getSecond()->d[2]);
getSection() returns a pointer to the vec object. When you then do ->d you are accessing the d array, within the vec object. Thus, when you add the [2] to the end, you are accessing the element at index 2 of the array, and are not calling operator[] of the vec object.
If you rewrite like this:
std::printf("%d\n", (*test2.getSecond())[2]);
Then the operator[] will be called on the vec object, and not its array. Note that you have to dereference the result of getSecond(). Alternatively, you can be more verbose:
std::printf("%d\n", test2.getSecond()->operator[](2));
Working example: https://godbolt.org/z/YWKzPz

Very good question!
The issue is that when you try to reference an item in an array via index, such as [2], you are actually referring to the size * 2 location. There is no built-in protection against it, but you can always check for \0 as that's where your arrays end. When you use arrays in C/C++, it is your job to make sure you are not outside of their location. It's generally a good idea to keep your array inside your structure/class and allow reaching its elements with setters and getters, which would handle the bounds and throw exceptions if those are violated.

Related

Known array / pointer size as argument

InputManager* input = new InputManager(new int[]{ SDLK_UP, SDLK_DOWN, SDLK_LEFT, SDLK_RIGHT });
I wanna pass this array of keys (or a pointer to it) but i need the InputManager's constructor to know its size. Since its known at compile time how would i get it?
Online i found this template
template <int N>
InputManager::InputManager(int (&keyArray)[N]) {
this->maxKeys = N;
this->keys = keyArray;
}
But i get an error that the arguments dont match.
I need any possible solution where i dont need to manually write the length. So macros, templates or anything else is accepted.
Don't use so many pointers. It's not healthy and you're likely to either leak memory, or try to free memory that you can't (if you didn't pass a newly-allocated array). Here's a minimal modification of your code:
class InputManager {
public:
template <int N>
InputManager(int (&keyArray)[N])
: maxKeys(N), keys(std::make_unique<int[]>(N))
{
std::copy_n(keyArray, N, keys.get());
}
private:
std::size_t maxKeys;
std::unique_ptr<int[]> keys;
};
int main() {
int keys[] = { 1, 5, 4, 7, 2 };
InputManager input {keys};
}
This compiles (GodBolt).
Notes:
This way, you don't need to write a custom destructor; although you would need to write an assignment operator which copies data; and a copy constructor.
It's probably better to just use an std::vector internally (or std::array if you know the input size beforehand), and take any span or range of keys in the constructor.
Wrap creating of this array into function using variadic template.
If you want only return a pointer to array:
template<class ... enums>
int* makeArray (enums ... e) {
return new int[sizeof...(e)]{e...};
}
When you want to know size of returned array you can return pair:
template<class ... enums>
std::pair< int*, size_t > makeArray2(enums ... e) {
constexpr size_t N = sizeof...(e);
return std::make_pair( new int[N]{e...}, N);
}

Vector in c++ error when trying to call at()

I'm trying to get a pointer to mytype of a vector(vector mytype*) but I get segmegation fault.
I've the following code:
void add(string b, vector<mytype*> *p){
int a;
mytype *fre=NULL;
a=oura.front();
oura.pop();
if(!(p->size()<a) && oura.size()>0){
fre=p->at(a-1); //Error seems to come from here
add(b,fre->get_vec());
}
else{ ...}
}
mytype was a class with a string and a vector<mytype*> pointer.
The error seems to come from fre=p->at(a-1)
oura is a queue<int> and takes values between 1-20 so i used (a-1).
at does bounds checking when accessing elements. If it's out of bounds it will throw an std::out_of_range exception.
adding an assert(a > 0); should let you find out when it happens.

Custom iterator out of bounds

I have an iterator class. Let's call it PIterator here. A MessageBuffer is iterated and is being outputted correctly, unless the nSizeOfMessage plus where the iterator currently points to is equal to the size of the whole message (position correct, index one too large).
If I check for the last element and decrement by one, it should work. Though it seems to be a "wrong way" to me. Yeah, I am not quite sure on this, so my problem is shown in this code snippet, maybe someone knows a good solution, tried to figure it out for quite a while.
Yes, I do know how to use a debugger, I know where the problem lies and it is explained just fine. I do not know how to fix this, unless used the way I mentioned.
This compiles fine under Visual Studio 2015.
Please also see the comments in the main function.
#include <iostream>
#include <vector>
class MessageBuffer
{
public:
MessageBuffer(const std::string &s)
{
_msgBuffer.assign(s.begin(), s.end());
}
char &operator[](std::size_t nIndex)
{
return _msgBuffer[nIndex];
}
//more functions...
private:
std::vector<char> _msgBuffer;
};
class PIterator
{
public:
PIterator(MessageBuffer &b)
: m_Ref(b)
, m_Where(0)
{ }
PIterator &operator=(PIterator &other)
{
if (this == &other)
return *this;
this->m_Ref = other.m_Ref;
this->m_Where = other.m_Where;
return *this;
}
//more functions...
PIterator operator+(unsigned int nValue) const
{
PIterator copy(*this);
copy.m_Where += nValue;
return copy;
}
PIterator &operator+=(unsigned int nValue)
{
m_Where += nValue;
return *this;
}
char &operator*()
{
return m_Ref[m_Where];
}
private:
MessageBuffer &m_Ref;
std::size_t m_Where;
};
int wmain(int argv, wchar_t **args)
{
std::string msg = "123MyMessage"; //Length 12
// ^ Index 3, Position 4
MessageBuffer mb(msg);
PIterator itr(mb);
//Calculations - here the results hardcoded
std::size_t nSizeOfMessage = 9; //The size of the message without the numbers
//itr.m_Where is 3 - That's where the non-numeric part of the message starts
itr += 3;
std::string needThis;
PIterator cpy = itr + nSizeOfMessage; //itr points to the first element of the message
//cpy is now out of bounds - position is correct, but index is 1 too large
needThis.assign(&*itr, &*cpy); //boom
return 0;
}
Instead of
needThis.assign(&*itr, &*cpy);
you need to use
needThis.assign(itr, cpy);
This will work if your PIterator satisfies iterator requirements.
The way you call assign, you pass pointers instead of iterators, which is valid by itself. But, to get the pointers, you dereference the iterators first. Dereferencing past-the-end iterator is undefined behavior, which is caught in Debug configuration of the compiler.
The solution I came up with was quite simple.
Instead of having a temporary iterator, I'll be using the char pointer and increment it's address by the size of the message, thus receiving always the correct last element. Should've seen that earlier.
needThis.assign(&*itr, (&*itr) + nSizeOfMessage);

Returning an invalid reference

Sometimes when I'm programming in C++ I wish there was an undefined value for every variable something like Javascript!.
For example when I'm returning a value for out-of-bounds element of an array, it was useful to return an undefined instead of throwing an exception, or:
template <typename T, int SIZE>
class MyArray
{
T arr[SIZE];
static T badref;
public:
T &operator[](int i)
{
if (i >=0 && i < SIZE)
return arr[i];
else
throw std::string("OUT-OF-BOUNDS"); // or: return badref; !!
}
};
Another dirty(In my opinion) option is returning a reference of a pre-defind variable as a bad-reference variable. I know we can not assign null or something like that to a reference variable.
Is there an another well formed pattern to return a reference where caller has the ability to find out the returned value is not valid?
EDIT: I'm not mean a pointer
You can use boost::optional as #chris mentioned in his comment. It comes as a part of Boost libary. See this page for more details.
Modified MyArray class:
template <typename T, int SIZE>
class MyArray
{
T arr[SIZE];
public:
optional<T&> operator[](int i)
{
if (i >=0 && i < SIZE)
return optional<T&>(arr[i]);
else
return optional<T&>();
}
};
Usage:
MyArray<int>() array;
// fill array with data
optional<int&> result = array[0];
if (result) {
// item was found
} else {
// index out of bounds
}
I wish there was an undefined value for every variable something like Javascript!
You only have an "undefined" value for pointers (nullptr). A reference is (by definition) something pointing to a valid instance.
To return a reference to a static object, you should separate between const and non-const values of your operator:
template <typename T, int SIZE>
class MyArray
{
T arr[SIZE];
static T badref;
public:
T &operator[](int i)
{
if (i >=0 && i < SIZE)
return arr[i];
else
// returning ref here would allow clients to write:
// MyArray<int> a;
// a[-1] = 5; // valid if you return a non-const reference
throw std::string("OUT-OF-BOUNDS");
}
const T &operator[](int i) const
{
if (i >=0 && i < SIZE)
return arr[i];
else {
// MyArray<int> a;
// a[-1] = 5; // will not compile (cannot assign to const)
static const T invalid = T();
return invalid;
}
}
};
Whatever you think of, your solution needs to fit into the type system. So your function signature must explicitly say (this way or another) that the result may be T, but it may be something else too.
Common ways for that are:
Instead of returning a value, return a status code and output the value via an "out" parameter (a pointer or reference):
bool tryGet(int i, T& result);
Return a tuple (status, value) like:
std::tuple<bool, T> get(int i)
(If couldn't get, consider the second tuple element irrelevant - requires T to have a default constructor)
Use boost::variant (flexible, but requires boost)
Use boost::optional (simpler version of the above, when you only need "either T or nothing")
The main goal of references is to avoid invalid (NULL) values while allowing functions to modify their arguments and keep from copying data. If you need a NULL value, use a pointer.

std::deque - subscript out of range

I am writing a plugin for SA-MP, based on AMX and have occured an annoying problem. I am using a deque and a function to find & delete an element. (like this one below)
enum PARAM_TYPE {
PARAM_TYPE_CELL,
PARAM_TYPE_ARRAY,
PARAM_TYPE_STRING,
};
struct params_s {
enum PARAM_TYPE type;
struct params_s * next;
cell free;
cell numData;
cell arrayData[0];
};
struct timer_s {
AMX * amx;
int id, func, interval, repeat;
long long unsigned int trigger;
struct params_s * params;
};
std::deque<struct timer_s *> gTimers;
void DestroyTimer(struct timer_s * t) {
for (int i = 0; i != gTimers.size(); ++i) {
if (t == gTimers[i]) {
gTimers.erase(gTimers.begin() + i);
break;
}
}
}
Whenever I call DestroyTimer() I get this error:
Debug Assertion Failed!
Expression: deque subscript out of range
I can add elements, read and modify them, but I can't delete them.
Thank you.
You should use the erase remove idiom:
void DestroyTimer(struct timer_s * t)
{
gTimers.erase(remove(gTimers.begin(), gTimers.end(), t), gTimers.end());
}
Without looking at the actual error, the idiomatic way would be:
gTimers.erase(std::remove(gTimers.begin(), gTimers.end(), t),
gTimers.end());
This will be safer and faster than what you are doing now (catches
duplicates, no need to reallocate).
This is called Erase-Remove idiom.
For the actual debug assertion: Debugging iterators are a standard
extension and maybe broken in some cases.
NB: You want to call delete on the timer, if it is owned by the deque, to prevent leaking memory.