С++ визов статических конструкторов
От: Andrij Украина  
Дата: 22.11.01 14:36
Оценка:
можно ли управлять порядком визова статических конструкторов спомощю линкера
make it simple as possible, but not simpler
Re: С++ визов статических конструкторов
От: Vodka  
Дата: 22.11.01 15:09
Оценка:
Здравствуйте Andrij, Вы писали:

A>можно ли управлять порядком визова статических конструкторов спомощю линкера


Это про VC++ 7.0 ?
Re: С++ визов статических конструкторов
От: IT Россия linq2db.com
Дата: 22.11.01 17:49
Оценка:
Здравствуйте Andrij, Вы писали:

A>можно ли управлять порядком визова статических конструкторов спомощю линкера


Нет, линкер понятия не имеет где у тебя статический конструктор, где C, где C++, а где ASM. Более того, порядок вызова конструкторов статических объектов реализован по разному разными версиями компиляторов. В частности VC++ делает это примерно следующим образом. Компилятор генерирует в каждом модуле одну или несколько процеду, которые собственно и вызывают конструкторы статических объектов. Далее адреса этих процедур помещаются в специальный сегмент с определённым именем. На C++ это могло бы выглядеть примерно так:

struct CTest {
    CTest(int n)
    {
        ::Sleep(n);
    }
} g_test(1000);

void __cdecl CTestInit()
{
    g_test.CTest(1000); // это типа вызов конструктора для g_test
}

typedef void (__cdecl *_PVFV)(void);
#pragma data_seg(".CRT$XCU")
_PVFV _init_module[] = { CTestInit };
#pragma data_seg()

Далее, в инициализирующем коде RTL имеется примерно такой код:

typedef void (__cdecl *_PVFV)(void);
#pragma data_seg(".CRT$XIA")    /* C initializers */
_PVFV __xi_a[] = { NULL };
#pragma data_seg(".CRT$XIZ")
_PVFV __xi_z[] = { NULL };
#pragma data_seg(".CRT$XCA")    /* C++ initializers */
_PVFV __xc_a[] = { NULL };
#pragma data_seg(".CRT$XCZ")
_PVFV __xc_z[] = { NULL };
#pragma data_seg(".CRT$XPA")    /* C pre-terminators */
_PVFV __xp_a[] = { NULL };
#pragma data_seg(".CRT$XPZ")
_PVFV __xp_z[] = { NULL };
#pragma data_seg(".CRT$XTA")    /* C terminators */
_PVFV __xt_a[] = { NULL };
#pragma data_seg(".CRT$XTZ")
_PVFV __xt_z[] = { NULL };
#pragma data_seg()  /* reset */

void __cdecl _initterm (_PVFV * pfbegin,_PVFV * pfend)
{
    /*
     * walk the table of function pointers from the bottom up, until
     * the end is encountered.  Do not skip the first entry.  The initial
     * value of pfbegin points to the first valid entry.  Do not try to
     * execute what pfend points to.  Only entries before pfend are valid.
     */
    while ( pfbegin < pfend )
    {
        /*
         * if current table entry is non-NULL, call thru it.
         */
        if ( *pfbegin != NULL )
            (**pfbegin)();
        ++pfbegin;
    }
}

... _DllMainCRTStartup(...)
{
    // ...

    /*
     * Do runtime startup initializers.
     */
    _initterm( &__xi_a, &__xi_z );


    /*
     * do C++ constructors (initializers) specific to this DLL
     */
    _initterm( &__xc_a, &__xc_z );

    // ...
}

Что здесь происходит?
Адреса всех процедур, вызывающих конструкторы и производящих другую инициализацию (типа CTestInit из первого примера), компилятор помещает в сегменты с именами от ".CRT$XCA" до ".CRT$XCZ". Затем при сборке линкер их тупо собирает в одну кучу и сортирует по именам сегментов, в результате получается массив указателей на функции. Инициализирующему коду остаётся только вызвать по очереди все функции, при этом порядок вызова этих функций неопределён и повлиять ты на него никак не можешь.

Но! Если нельзя, но очень хочется, то можно

Зная детали процесса инициализации, можно вполне их использовать. В частности можно написать свои процедуры, которые будут вызываться до или после отработки конструкторов статических объектов:

#include "stdafx.h"
#include <windows.h>
#pragma comment(lib,"user32")

struct CTest {
    CTest()
    {
        ::MessageBox(NULL,TEXT("CTest"),TEXT("test"),0);
    }
} g_test;

void __cdecl f2()
{
    ::MessageBox(NULL,TEXT("f2"),TEXT("test"),0);
}

void __cdecl f1()
{
    ::MessageBox(NULL,TEXT("f1"),TEXT("test"),0);
}

typedef void (__cdecl *_PVFV)(void);
#pragma data_seg(".CRT$XIZ")
static _PVFV _xi_z[] = { f1 };
#pragma data_seg(".CRT$XCZ")
static _PVFV _xc_z[] = { f2 };
#pragma data_seg()

int main(int argc, char* argv[])
{
    return 0;
}

В этом примере f1 отработает до конструктора CTest, а f2 после.

На BC++ это решается примерно таким же образом, только там есть специальная прагма, позволяющая задавать приоритет вызова для конкретных процедур. Если мне не изменяет мой склероз, то это что-то типа

#pragma startup startfuncname priority
#pragma exit    exitfuncname  priority
Если нам не помогут, то мы тоже никого не пощадим.
Re: С++ визов статических конструкторов
От: Andrij Украина  
Дата: 22.11.01 18:34
Оценка:
Andrij:
Спасибо Всем!

A>можно ли управлять порядком визова статических конструкторов спомощю линкера


В VC 6.0 я решил ету проблему следующим образом :
вынес определение всех глобальних статических обектов в либы
и порядок вызова их конструкторов определяю порядком включения в проект либ -- сработало!
make it simple as possible, but not simpler
Re[2]: С++ визов статических конструкторов
От: Alex Fedotov США  
Дата: 22.11.01 18:36
Оценка:
Здравствуйте IT, Вы писали:

A>>можно ли управлять порядком визова статических конструкторов спомощю линкера


IT>Нет, линкер понятия не имеет где у тебя статический конструктор, где C, где C++, а где ASM.


[... превосходное разъяснение безжалостно пропущено ...]

IT>typedef void (__cdecl *_PVFV)(void);
IT>#pragma data_seg(".CRT$XIZ")
IT>static _PVFV _xi_z[] = { f1 };
IT>#pragma data_seg(".CRT$XCZ")
IT>static _PVFV _xc_z[] = { f2 };
IT>#pragma data_seg()


Боже, ну зачем так сложно, в Visual C++ тоже есть прагмы для этого:

#pragma init_seg(compiler)
#pragma init_seg(lib)
#pragma init_seg(user)
-- Alex Fedotov
Re[3]: С++ визов статических конструкторов
От: IT Россия linq2db.com
Дата: 23.11.01 01:24
Оценка:
Здравствуйте Alex Fedotov, Вы писали:

AF>Боже, ну зачем так сложно, в Visual C++ тоже есть прагмы для этого:


AF>#pragma init_seg(compiler)

AF>#pragma init_seg(lib)
AF>#pragma init_seg(user)

Это да, но это только для C++, и только для init. Для exit, конечно, можно и даже лучше использовать atexit. Но управлять приоритетом возможности практически нет, хотя на практике, в C++ (на C были прецеденты), мне это так и не понадобилось.
Если нам не помогут, то мы тоже никого не пощадим.
Re[4]: С++ визов статических конструкторов
От: Alex Fedotov США  
Дата: 23.11.01 02:07
Оценка:
Здравствуйте IT, Вы писали:

IT>Здравствуйте Alex Fedotov, Вы писали:


AF>>Боже, ну зачем так сложно, в Visual C++ тоже есть прагмы для этого:


AF>>#pragma init_seg(compiler)

AF>>#pragma init_seg(lib)
AF>>#pragma init_seg(user)

IT>Это да, но это только для C++, и только для init. Для exit, конечно, можно и даже лучше использовать atexit. Но управлять приоритетом возможности практически нет, хотя на практике, в C++ (на C были прецеденты), мне это так и не понадобилось.


Погоди, в языке C до main ничего не выполняется, или я что-то путаю? Это же только в C++ появилось

// at the global scope
int n = my_cool_function();

Что касается порядка вызова деструкторов, то он обратный порядку вызова конструкторов. На самом деле реализация статической инициализации в Visual C++ вызывает atexit для регистрации деструкторов.
-- Alex Fedotov
Re[5]: С++ визов статических конструкторов
От: IT Россия linq2db.com
Дата: 23.11.01 03:51
Оценка:
Здравствуйте Alex Fedotov, Вы писали:

AF>Погоди, в языке C до main ничего не выполняется, или я что-то путаю? Это же только в C++ появилось


Пришлось поднять свои древние исходники... Действительно, боролся я с порядком деинициализации

AF>Что касается порядка вызова деструкторов, то он обратный порядку вызова конструкторов. На самом деле реализация статической инициализации в Visual C++ вызывает atexit для регистрации деструкторов.


Тем не менее VCRTL использует atexit в редких случаях. И опять же, вопрос стоял о приоритетах вызова всего этого безобразия.
Если нам не помогут, то мы тоже никого не пощадим.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.