Разминка для мозгов: "Hello, World!"

atexit() — принимается.

Только я не понял зачем ты такую сложную программу написал. Вполне хватит и

#include <iostream>
#include <cstdlib>

void foo()
{
   std::cout << "hello, world" ;
}

int x = atexit(foo);

int main( )
{
   return 0 ;
}

Описалово функции atexit().

И это ещё не всё.

Описалово функции atexit().

Wow! Какой хороший сайт! Надеюсь его не заблокируют за нарушение авторских прав!!!

Только я не понял зачем ты такую сложную программу написал. Вполне хватит и

ну так тема же:

Разминка для мозгов: «Hello, World!»

вот пусть, разминают. Я на таких задачках уже наразминался вдоволь )))

Croessmah, понимаешь, «разминка для мозгов» — это предложение подумать над неочевидными решениями вроде вполне очевидных задач. И чёткость решения здесь важна.

Вот, например, задача из этого топика показывает, что выполнение программы начинается не с функции main(), и ей не заканчивается. Следовательно при отладке реальных программ на эти моменты тоже надо обращать внимание. Что для начинающих может быть совершенно не очевидно.

А по теме топика можешь добавить что-то принципиально отличное от уже предложенного?

Описалово функции atexit().

Интересно не знать, что делает эта ф-ция, а уметь написать её самому. Мне кажется, или без грязной ассемблерной вставки, в форме запихивания в стек адрес функций (иначе как объяснить порядок выполнения?), тут не обошлось?

Думаю, что написать ее самому проблематично. Надо знать адрес (точнее говоря, символическое имя этого адреса) стека функций завершения. Маловероятно, что для этих целей используется системный стек. Скорее всего, стек для функций завершения отдельный на 32 адреса, а в программе при завершении вызывается некая функция (часть си-шной RTS), которая проверяет наличие в этом стеке адресов функций и, если таковые имеются в цикле вызывает эти функции до исчерпания стека.

А по теме топика можешь добавить что-то принципиально отличное от уже предложенного?

Принципиально отличное врядли возможно в соответствии со стандартом. Будут лишь вариации одного и того же, например, как это:

#include <iostream>

struct X
{
   void * operator new (std::size_t size)
   {
      std::cout <<"hello world" << std::endl ;
      return ::operator new(size) ;
   }
} ;

X * x = new X ;

int main()
{
   return 0 ;
}

...

#include <vector>
#include <iostream>
#include <memory>

template <typename T>
class Alloc : public std::allocator<T>
{
public:
   typedef typename std::allocator<T>::pointer pointer ;
   typedef typename std::allocator<T>::size_type size_type ;
   typedef typename std::allocator<T>::value_type value_type ;
   typedef typename std::allocator<T>::reference reference ;
   typedef typename std::allocator<T>::const_pointer const_pointer ;
   typedef typename std::allocator<T>::const_reference const_reference ;
   typedef typename std::allocator<T>::difference_type difference_type ;
   Alloc()
     : std::allocator<T>()
   {
      std::cout << "hello world" << std::endl ;
   }
} ;

std::vector< int,Alloc<int> > f(1) ;

int main()
{
   return 0 ;
}

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

Думаю, что написать ее самому проблематично

Такого быть не может. По вашему стандартая библиотека неявно становится частью «сырого» языка, но ведь это не так. (не будем вспоминать std::type_info, возвращаемый typeid)

По вашему стандартная библиотека неявно становится частью «сырого» языка

На счёт «сырого» я бы поспорил... А в остальном, оно так и есть.

На самом деле, так было задумано изначально. Поскольку С разрабатывался как высокоуровневый кроссплатформенный «ассемблер», то все платформозависимые вещи выносились в стандартную библиотеку, начиная от size_t и заканчивая printf().

Реализация стандартной библиотеки уникальна для каждого компилятора (и, естественно, платформы). И, скорее всего, написана она на 90% на ассемблере. (Конечно можно printf реализовать на С, а вот «variadic functions» — уже явно ассемблер.)

Croessmah, то, что ты написал — действительно вариации на темы инициализации глобальной переменной и конструктора глобального объекта.

назначение стандартных обработчиков для разных ситуаций.

А вот это момент более интересный.

Пока были перечислены следующие способы выполнения кода вне функции main():
1. Конструктор глобального объекта.
2. Деструктор глобального объекта.
3. Инициализация глобальной переменной с помощью вызова подпрограммы.
4. Функция-обработчик нормального завершения программы (посредством вызова atexit()).

А в остальном, оно так и есть.

Печально, когда так или иначе приходится использовать стандартную библиотеку :(

На счёт «сырого» я бы поспорил...

А что не так в «сыром»?

Видимо как-то можно обойтись и без стандартной библиотеки. Пишут же операционки на С. Согласен, что в этом случае без ассемблера не обходится, но это, видимо, совсем низкий уровень: загрузчик, планировщик и т.п. А когда пишут ОС с нуля, там по началу даже системных вызовов нет. Т.е. исполняемые модули новой ОС должны быть написаны на чистом С (как альтернатива ассемблеру) без использования стандартной библиотеки с минимальными прологами и эпилогами. Причем прологи и эпилоги должны удовлетворять соглашениям новой ОС.

porshe, несколько странно называть «сырым» язык с почти 45-летней историей ))

А чего печального в использовании стандартной библиотеки?

несколько странно называть «сырым» язык с почти 45-летней историей ))

Похоже, есть различие в понимании терминов. «Сырой» значит голый язык, то есть язык без библиотек, с одними только инстсрукциями и операторами.

А чего печального в использовании стандартной библиотеки?

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

Наверное надо подвести итоги, поскольку новых идей больше не появляется.

Добавлю от себя пять копеек )) Ни кто не вспомнил про функцию-обработчик аварийного завершения программы при необработанном исключении (посредством вызова функции set_terminate()).

На эту же тему есть unexpected() и set_unexpected(). Объявлены deprecated. Перехват управления осуществляется только в случае, когда в функции происходит исключение, которое непродекларировано в списке исключений для этой функции. В MSVC++ 2013 не поддерживается.

Ещё есть инициализация статического члена класса. Причём создавать объект класса не обязательно. Но этот случай, по сути, очень близок к инициализации глобальной переменной. Поэтому отдельным пунктом не вынесен.

Итоговый текст программы:

#include <iostream>
#include <cstdlib>
#include <exception>

using namespace std;

// Инициализация глобальной переменной посредством вызова функции

int func() {
    cout << "initialization of global variable by function call (1)" << endl;
    return 0;
}

int a = func();

// Инициализация глобальной переменной посредством выражения с побочным эффектом

ostream *os = &(cout << "initialization of global variable by expression with side effect (2)" << endl);

// Конструктор и деструктор глобального объекта

class Class {
public:
    Class();
    ~Class();
};

Class::Class() {
    cout << "constructor of global object (3)" << endl;
}

Class::~Class() {
    cout << "destructor of global object (4)" << endl;
}

Class b;

// Функция-обработчик завершения программы (посредством вызова atexit())

void atexit_func() {
    cout << "program termination handler via atexit() (5)" << endl;
}

int u = atexit(atexit_func);


// Функции-обработчики аварийного завершения программы при необработанном исключении 
// (посредством вызовов функций set_unexpected() и set_terminate())


void unexp() {
  cout << "unhandled exception handler via set_unexpected() (7)" << endl;
  terminate();
}

unexpected_handler uf = set_unexpected(unexp);

void term() {
    cout << "unhandled exception handler via set_terminate() (6)" << endl;
    exit(1);
}

terminate_handler tf = set_terminate(term);

int raise_except() throw(double) {
    throw 1;    // исключение int, продекларировано только double
    return 0;
}

int t = raise_except();


// точка входа ))

int main()
{
    return 0;
}

Хотя... возможно где-то что-то ещё есть на эту тему. Продолжение следует? ;-)

Ни кто не вспомнил про функцию-обработчик аварийного завершения программы при необработанном исключении (посредством вызова функции set_terminate()).

Я ж писал:

назначение стандартных обработчиков для разных ситуаций.

)))

Croessmah, ты не уточнил что ты имеешь ввиду под "обработчиками для разных ситуаций". Один случай ты показал — atexit(). Перехват terminate() — совсем другая область и другой механизм работы. Если ты знаешь ещё какие-то обработчики — конкретизируй.

Один случай ты показал — atexit(). Перехват terminate() — совсем другая область и другой механизм работы.

Смысл я вкладывал не в конечный механизм, а в то, что мы используем переменную со статическим временем хранения(в данном случае глобальную) для того, чтобы вызвать код, который назначит обработчик и потом спровоцирует его вызов.

Если ты знаешь ещё какие-то обработчики — конкретизируй.

Да тот же set_new_handler

Продолжение следует? ;-)

А своя реализация atexit считается способом? :)

#include <iostream>
#include <vector>


typedef void(*fvoidptr_t)();

namespace not_used
{

    class __not_used_class__
    {
    public:
        ~__not_used_class__()
        {
            for ( int i = funcs.size()-1; i >= 0; i--)
                funcs[i]();
        }

        void _atexit( fvoidptr_t func )
        {
            funcs.push_back( func );
        }

    private:
        std::vector<fvoidptr_t> funcs;

    }_not_used_ob_;

}


int atexit( fvoidptr_t func )
{
    not_used::_not_used_ob_._atexit( func );
    return 0;
}

void hello()
{
    std::cout << "Hello";
}

void world()
{
    std::cout << " world!" << std::endl;
}

int u = atexit( world );
int u2 = atexit( hello );

int main()
{
    return 0;
}

Правда, тут опять же используется деструктор глобального объекта, но зато сам написал, без этих ваших библиотек!1!1 (шутка, конечно).

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

Пожалуйста, оформляйте исходный код в соответствии с правилами разметки. Для того, чтобы вставить код в комментарий, скопируйте его в текстовое поле ниже, после чего выделите то, что скопировали и нажмите кнопку «код» в панели инструментов. Иначе ваш код может принять нечитаемый вид.

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.