Признатся, я перешел с C++ на Java во многом именно из-за проблем с памятью. Но, кроме сборщика мусора, мне в Java импонирует еще одна вещь: в случае возникновения исключения во время исполнения программы, Java распечатывает стек вызовов! Устраняя глюки (те, что вызывают исключения) в программах на Java, я даже отладчик не использовал, и долгое время (первый год) не пользовался им вообще. После C++ это было что-то!!! Сообщения об ошибках в C++ которые выплевывались при генерации исключений и распечатка содержимого стека (одни цифири) не давали непосвященному вроде меня почти никакой информации о том, где произошла ошибка. Особенно, если использовались шаблоны, а ошибка происходила где-нибудь в debug/vector. В этой статейке я расскажу о своих попытках распечатки стека вызовов в C++ (в случае генерации исключения), в таком же виде, в каком я привык это видеть в Java. Специально для этой этого я написал маленькую программку (надеюсь, моя статья не такая паршивая, как эта программа :)))).
#include <stdio.h>
#include <vector>
void fillDoubleVector(vector<double> &vect){
vect.resize(20);
unsigned size = vect.size();
for(int i=size-1; i >= 0; i++)//Ошибка
vect[i] = i*(double)size - size/(double)(i+1);
}
//-----------------------------------------------------
void fillLongVector(vector<long> &vect){
vector<double> dvect;
fillDoubleVector(dvect);
unsigned size = dvect.size();
vect.resize(size);
for(int i=size-1; i >= 0; i++)//Ошибка
vect[i] = i*size - size/(i+1);
}
//-----------------------------------------------------
int main(int n, const char **args){
vector<long> lvect;
fillLongVector(lvect);
for(int i=0; i < lvect.size(); i++)
printf("lvect[%i] = %i\n", i, lvect[i]);
return 0;
}
Программу сохранил в файле test.cpp откомпилил в MinGW:
g++ -o test.exe -D _GLIBCXX_DEBUG -g3 test.cpp
Определил _GLIBCXX_DEBUG , что бы в программе использовалась отладочная версия STL (которая хотя бы сообщает о таких ошибках, как выход за пределы диапазона). Получил вот такое сообщение об ошибке:
C:/DEV-CPP/BIN/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/
debug/vector:192:
error: attempt to subscript container with out-of-bounds index 20,
but container only holds 20 elements.
Objects involved in the operation:
sequence "this" @ 0x0074FDC0 {
type = N15__gnu_debug_def6vectorIdSaIdEEE;
}
abnormal program termination
Замечательное и очень информативное сообщение об ошибке! Только местоположение ошибки указано весьма туманно. Было бы еще змечательно, что бы после этого еще и стек вызовов распечатывался. Переделал я эту программулину, вставив в начало и конец каждой функции блоки try-catch:
#include <stdio.h>
#include <exception>
using namespace std;
#include <vector>
//Класс-исключение:
class MyException : public exception
{
public:
MyException(const char *msg, int line)
{
printf("Error in file %s : %i\n", msg, line);
}
};
//--------------------------------------------------
void fillDoubleVector(vector<double> &vect){
try{
vect.resize(20);
unsigned size = vect.size();
for(int i=size-1; i >= 0; i++)//Ошибка
vect[i] = i*(double)size - size/(double)(i+1);
}catch(exception ex){
throw MyException(__FILE__, __LINE__);
}
}
//-----------------------------------------------------
void fillLongVector(vector<long> &vect){
try{
vector<double> dvect;
fillDoubleVector(dvect);
unsigned size = dvect.size();
vect.resize(size);
for(int i=size-1; i >= 0; i++)//Ошибка
vect[i] = i*size - size/(i+1);
}catch(exception ex){
throw MyException( __FILE__, __LINE__);
}
}
int main(int n, const char **args){
try{
vector<long> lvect;
fillLongVector(lvect);
for(int i=0; i < lvect.size(); i++)
printf("lvect[%i] = %i\n", i, lvect.at(i));
}catch(exception ex){
printf("Error in file %s : %i\n", __FILE__, __LINE__);
}
return 0;
}
Откомпилил, запустил - млин, результат нисколько не изменился!!! Чертовщина, блин! Похоже, что в vector при выходе за пределы массива не выбрасывается исключение, во всяком случае в реализации MinGW. Полез в заголовочные файлы (исходников всей библиотеки, к сожалению, не было) - так и есть, не выбрасывается даже в отладочной версии.
Потом уже прочел в описании STL, что оператор индексирования [] исключение не выбрасывает и проверок никаких не делает, но зато есть функция-член at(), которая работает точно так же как оператор [], но проверки делает и исключение выбрасывает.
Отладочную версию файла vector я несколько раскурочил (перекрестившись и сохранив его копию), вставив в тело функции operator [] вызов функции at() и добился-таки желаемого:
Error in file test2.cpp : 23
Error in file test2.cpp : 36
Error in file test2.cpp : 47
Жаль, что старое информативное сообщение об ошибке уже не выводится. Но все можно исправить, для этого надо немного поковырять исходники библиотек(насколько я понимаю - вставить вместо вызова функции abort() генерацию исключения). Найти бы тока время, что бы этим заняться, и трафик, что бы их (stdlibc++ sources) скачать :)
Еще в дополнение замечу, что кроме выходов за границы есть и другие ошибки, которые можно было бы так отлавливать - главное, что бы при их возникновении выкидывалось исключение. Конечно, ручками вставлять кучу блоков try/catch вряд ли кто захочет, да и вряд ли это нужно делать ручками. Но вот если написать такую приблуду, которая будет сама это в исх. код вставлять, потом все это компилить, а потом из исходного кода удалять... Напишите для меня такую, кто-нибудь, заплатить не обещаю, но обещаю 100 раз скачать :)
1 коммент.:
Посмотрите на STLport.
В debug-режиме эта версия STL умеет как раз то, что вы ищете.
Отправить комментарий