понедельник, 17 ноября 2008 г.

__gnu_cxx и __verbose_terminate_handler

Отвлечемся от std. Рассмотрим пространство имен __gnu_cxx, в котором объявлен __verbose_terminate_handler, позволяющий получить больше информации о возникающей исключительной ситуации. Замечу, что это все тот же заголовочный файл exception.
Рассмотрим следующий код:

int main (int argc, char * const argv[]) {
std::cout << "Hello, World!\n";
std::set_terminate(__gnu_cxx::__verbose_terminate_handler);
int k=120;
if(k>100)
throw std::exception();
else
throw k;
for(int i=k;i<1000;++i)
std::cout<<".";
return 0;
}

На выходе получаем следующее:

[Session started at 2008-11-17 23:42:53 +0300.]
Hello, World!
terminate called after throwing an instance of 'std::exception'
what(): St9exception

Как видно из примера, при возникновении исключительной ситуации выводится дополнительная информация о ней, что в некоторых случаях может быть полезным.

Использование set_terminate

Решил немного написать по поводу set_terminate.
Функция set_terminate объявлена в заголовочном файле exception. Она имеет следующий вид:

terminate_handler set_terminate(terminate_handler) throw().

В качестве параметра функция берет указатель на функцию, которая будет вызвана в случае терминирования программы.
Данный указатель на функцию имеет следующий вид:

void (* pF)(void).

Если мы через функцию set_terminate устанавливаем новый обработчик, то она вернет нам старый, если ранее такой существовал для данной ситуации.
Возникает вопрос: а зачем нужно использовать set_terminate? В ответ можно сказать только следующее, что при ошибке в программе, которая ведет к терминированию этой программы, может возникнуть необходимость сохранить данные, закрыть файлы, записать в лог информацию о причинах ошибки и т.д.
В заключении привожу пример использования:

#include <iostream>
#include <exception>

void func1()
{
std::cout<<"The end"<<std::endl;
}

int main (int argc, char * const argv[]) {
std::cout<< "Hello, World!\n";
std::set_terminate(func1);
throw std::exception();
return 0;
}

В результате работы программы получаем следующее:

[Session started at 2008-11-17 23:10:04 +0300.]
Hello, World!
The end

понедельник, 10 ноября 2008 г.

Книга по С++, которую я прочитал первой

Нашел я и книгу, которую прочитал первой при изучении языка С++. Она мне нравилась простотой и последовательностью изложения. Ее удобно использовать, изучая язык. Мне это в ней и нравилось. В настоящее время я, к сожалению, ее уже не имею в своем распоряжении, чтобы подробнее описать. Причина отсутствия: "подарил школьной библиотеке". Так что в школе, где я учился и работал, стало на одну книгу по С++ больше. "Отличный язык в широкие массы!!!".
А вот и ссылка на книгу:
Язык Си++
В. В. Подбельский

Язык Си++

Книга по С++

На вопрос: "Какую книгу почитать по С++?", я предлагаю прочитать книгу Бьерна Страуструпа. Книга интересная, глубокая. Можно прочитать ее поверхностно, выяснив только те вопросы, которые Вас интересуют, а можно копнуть глубоко, прочитав "от корки до корки". Использовать ее как справочник мне очень нравится. Четкое изложение, наличие примера - все что надо для "прояснения вопроса".

Язык программирования С++
Бьерн Страуструп

Язык программирования С++. Специальное издание


Эту книгу я прочитал не первой, однако, купив ее, пользуюсь постоянно.

Перегрузка операторов перенаправления из потока и в поток

Когда мы используем вывод на консоль, то часто пишем следующий код:
std::cout<<"Hello, world"<<std::endl

Предположим, что мы создали класс следующего вида:
class TestClass{
public:
TestClass();
~TestClass();
private:
int i;
std::string s;
};

Для вывода содержимого класса на экран можно воспользоваться созданным специально для этого методом:
class TestClass{
public:
TestClass();
~TestClass();
void output(); // метод для вывода на консоль
private:
int i;
std::string s;
};

void TestClass::output(){
std::cout<<"i="<<i<<"s="<<s<<std::endl;
}

Тогда вывод для экземпляра будет выглядеть так:
TestClass tc;
tc.output();

Такой вариант возможен, но он очень уж некрасив. Переопределим оператор перенаправления в поток, что позволит нам перенаправлять содержимое экземпляра класса в поток вывода:
TestClass tc;
std::cout<<tc<<std::endl;

Для этого реализуем друга класса следующего вида:
class TestClass{
public:
TestClass();
~TestClass();
friend std::ostream& operator<<(std::ostream& out, const TestClass& c);
private:
int i;
std::string s;
};
std::ostream& operator<<(std::ostream& out, const TestClass& c);

Что содержится в теле данного оператора? В нем должен быть реализован функционал по перенаправлению полей класса в поток вывода, например:
std::ostream& operator<<(std::ostream& out, const TestClass& c)
{
out<<"i="<<c.i;
out<<" s="<<c.s;
return out;
}
Т.к. оператор является другом класса, то он имеет доступ к приватным полям класса, что не требует от нас делать их публичными.

Теперь, используя написанный код, можно выводить содержимое класса на консоль:

TestClass tc;
std::cout<<tc<<std::endl;
TestClass *p=new TestClass();
std::cout<<"second: "<<*p<<std::endl;

пятница, 7 ноября 2008 г.

Еще одна книга

Читать на русском хорошо. Но читать техническую литературу на английском приходится чаще. Можно попрактиковаться при чтении стандарта языка С на английском языке. Соответствующая ссылка приведена ниже:
The C Standard
Standards Institute British
The C Standard : Incorporating Technical Corrigendum 1

Если успеете запастись данной книгой на выходные, то поделитесь своими впечатлениями на тему "Мои английские выходные".

Интересная книга

Хочется поделиться еще одной интересной книгой, которая в настоящий момент доступна для чтения:

C Primer Plus

Стивен Прата

Язык программирования C. Лекции и упражнения


В данной книге очень хорошо и подробно рассмотрен язык. Уделено внимание рассмотрению конкретных примеров. В принципе, эта книга может быть интересно разноуровневым специалистам. Написана известным человеком в данной области. Так что книга "на выходные" есть.

Использование функции _Exit

Рассмотрим второй вариант завершения программы.

Для завершения программы без вызова обработчиков, зарегистрированных с помощью функции atexit, можно использовать функцию _Exit. Данная функция задекларирована следующим образом:
void _Exit(int).

В качестве параметра функция принимает код возврата, с которым программа и завершается.
Как я уже упоминал: функции-обработчики не вызываются. Рассмотрим пример кода:

int k=0;

void exit1(){
printf("At function exit1 %d\n",k++);
}

void exit2(){
printf("At function exit2 %d\n",k++);
}

void exit3(){
printf("At function exit3 %d\n",k++);
}

void exit4(){
printf("At function exit4 %d\n", k++);
}

typedef void (* Func_t)(void);

int main (int argc, const char * argv[]) {

Func_t arr[4]={exit1, exit2, exit3, exit4};

int i=0;

for(i=0;i<4;++i)
atexit(arr[i]);

for(i=0;i<10;++i)
if(i==5){
printf("%d",i);
_Exit(0);
}
printf("End of program");
return 0;
}

При выполнении данного кода произойдет следующее. При i==5 программа завершит свое выполнение путем вызова функции _Exit, при этом ни один из обработчиков вызван не будет. Результат выполнения приведен ниже:

[Session started at 2008-11-07 11:10:00 +0300.]

The Debugger has exited with status 0.

четверг, 6 ноября 2008 г.

Использование функции exit

Рассмотрим варианты выхода из программы.

Первый вариант - это выход при помощи вызова функции exit, которая выглядит следующим образом:
void exit(int)

В качестве параметра в функцию нужно передать код возврата, с которым закончит свою работу программа.
При вызове функции программа завершает свою работу. При этом вызываются все функции, зарегистрированные с помощью функции atexit.

Пример использования:

int k=0;

void exit1(){
printf("At function exit1 %d\n",k++);
}

void exit2(){
printf("At function exit2 %d\n",k++);
}

void exit3(){
printf("At function exit3 %d\n",k++);
}

void exit4(){
printf("At function exit4 %d\n", k++);
}

Это функции - обработчики завершения программы.

typedef void (* Func_t)(void);

int main (int argc, const char * argv[]) {

Func_t arr[4]={exit1, exit2, exit3, exit4};

int i=0;

for(i=0;i<4;++i)
atexit(arr[i]);

for(i=0;i<10;++i)
if(i==5){
printf("%d",i);
exit(0);
}
printf("End of program");
}

При запуске данного кода получаем следующий результат:

5 // Печатается счетчик цикла i
At function exit4 0 // Результат выводится последним обработчиком
At function exit3 1 // Результат выводится предпоследним обработчиком
At function exit2 2 // и т.д.
At function exit1 3

вторник, 4 ноября 2008 г.

Что можно почитать?

Часто возникает вопрос: А где найти информацию по языку, стандарту и т.д. В принципе, таких мест много в сети. Но если удобно работать с печатными изданиями, которые всегда под рукой, "приятно шелестят страницами", не требуют наличия соединения с сетью, да и вообще ноутбука, то я могу порекомендовать следующее:
Язык программирования C
Сэмюел П. Харбисон, Гай Л. Стил
Язык программирования C

Книга, а мой взгляд, написана очень хорошо. Полностью раскрыты возможности использования языка, приведены примеры.
Так что, эта книга заслуживает Вашего прочтения.

понедельник, 3 ноября 2008 г.

Дополнение по atexit

По поводу 80 обработчиков:
хочу уточнить, что в первом тестовом примере было вызвано 200 обработчиков, а во втором вообще 1600.

Дополнение по atexit

Я протестировал возможность добавления 32 обработчиков выхода через atexit. В результате тестов я обнаружил, что i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5465) позволяет мне добавить до 80 обработчиков. Тестовый код приведен ниже:

int k=0;


void exit1(){

printf("At function exit1 %d\n",k++);

}

int main (int argc, const char * argv[]) {

int i=0;

for(i=0;i<400;++i)

atexit(exit1);

for(i=0;i<100;++i)

printf(".");

    return 0;

}


На выходе получаем:

At function exit1 1

At function exit1 2

At function exit1 3

At function exit1 4

...

At function exit1 196

At function exit1 197

At function exit1 198

At function exit1 199


Если мы используем следующий код:

int k=0;


void exit1(){

printf("At function exit1 %d\n",k++);

}


void exit2(){

printf("At function exit2 %d\n",k++);

}


void exit3(){

printf("At function exit3 %d\n",k++);

}


void exit4(){

printf("At function exit4 %d\n"k++);

}


typedef void (* Func_t)(void);


int main (int argc, const char * argv[]) {

    // insert code here...

Func_t arr[4]={exit1, exit2, exit3, exit4};

    printf("Hello, World!\n");

int i=0;

int j=0;

for(j=0;j<400;++j)

for(i=0;i<4;++i)

atexit(arr[i]);

for(i=0;i<100;++i)

printf(".");

    return 0;

}


В этом случае результат еще интереснее:

...

At function exit4 1596

At function exit3 1597

At function exit2 1598

At function exit1 1599


Это говорит о том, что количество функций-обработчиков - не ограничено. Все зависит от конкретной реализации. 

Однако, согласно стандарту, надо помнить, что гарантировано вызывается только 32 обработчика.

Использование atexit

Использование функции atexit... Функция позволяет назначать обработчики выхода из программы. Зарегистрированные с помощью нее обработчики будут вызваны при нормальном завершении программы, а также в некоторых случаях прерывания ее работы. Это позволяет провести некоторые операции, например, сохранение данных программы, при ее завершении.
Функция atexit определена в стандартом заголовочном файле stdlib.h. Функция выглядит следующим образом:

int atexit(void (*)(void)).


Таким образом, функция, которая может использоваться в качестве обработчика выхода из программы, не должна возвращать результатов и, кроме того, она не имеет параметров.


Пример использования:


#include

#include


void exit1(){

printf("At function exit1\n");

}


void exit2(){

printf("At function exit2\n");

}


void exit3(){

printf("At function exit3\n");

}


void exit4(){

printf("At function exit4\n");

}


int main (int argc, const char * argv[]) {

    // insert code here...

    printf("Hello, World!\n");

atexit(exit1);

atexit(exit2);

atexit(exit3);

atexit(exit4);

int i=0;

for(i=0;i<100;++i)

printf(".");

    return 0;

}

В результате получается следующее:

[Session started at 2008-11-03 22:45:28 +0300.]

Hello, World!

....................................................................................................At function exit4

At function exit3

At function exit2

At function exit1


Можно зарегистрировать до 32 обработчиков. При этом они будут вызываться в обратном порядке, т.е. последний зарегистрированный обработчик будет вызываться первым.

О блоге...

Данный блог посвящен программированию на языке С. В нем я буду приводить примеры использования стандартного С (с99), а также рассматривать некоторые интересные моменты...