[Trick] Конструктор класса
От: remark Россия http://www.1024cores.net/
Дата: 03.09.08 19:50
Оценка: 264 (28) +1 :)
Вначале пример использования:
struct A : class_initializer<A>
{
    static void static_ctor()
    {
        std::cout << __FUNCTION__ << std::endl;
    }

    static void static_dtor()
    {
        std::cout << __FUNCTION__ << std::endl;
    }
};

struct B : class_initializer<B>
{
    static void static_ctor()
    {
        std::cout << __FUNCTION__ << std::endl;
    }

    static void static_dtor()
    {
        std::cout << __FUNCTION__ << std::endl;
    }
};

int main()
{
    std::cout << __FUNCTION__ << std::endl;
}


Выводит:
A::static_ctor
B::static_ctor
main
B::static_dtor
A::static_dtor


Теперь реализация (для любителей хардкорного С++):
template<typename derived_t, typename target_t = derived_t>
class class_initializer
{
    struct helper
    {
        helper()
        {
            target_t::static_ctor();
        }

        ~helper()
        {
            target_t::static_dtor();
        }
    };

    static helper helper_;

    static void use_helper()
    {
        (void)helper_;
    }

    template<void(*)()>
    struct helper2 {};

    helper2<&class_initializer::use_helper> helper2_;

    virtual void use_helper2()
    {
        (void)helper_;
    }
};

template<typename derived_t, typename target_t>
typename class_initializer<derived_t, target_t>::helper 
    class_initializer<derived_t, target_t>::helper_;


Основная идея достаточна прямолинейна — создаём статическую переменную, конструктор которой вызывает static_ctor(), а деструктор static_dtor(). Но дьявол, как говорится, в деталях. Как заставить компилятор инстанциировать статическую переменную шаблонного класса, так сказать, "изнутри" этого же класса? У меня на это ушло примерно 2 года непрерывных медитаций
Понятно, что надо как-то использовать эту статическую переменную (helper_), что бы заставить компилятор её инстанциировать. Со студией всё достаточно просто. Достаточно создать виртуальную функцию (use_helper2), которая упоминает статическую переменную. По стандарту будет ли инстанциироваться виртуальная функция — implementation defined. Но студия инстанциирует, поэтому всё замечательно.
Основная засада с gcc, т.к. он, сволочь, не инстанциирует виртуальные функции шаблонных классов без особой надобности. При наследовании от шаблонного класса гарантированно по стандарту инстанциируются только объявления всех членов. Поэтому надо как-то из объявления члена класса использовать статическую переменную. Решение следующее. Параметризуем вспомогательный шаблонный класс (helper2) адресом статической функции (use_helper). Поскольку от функции берётся адрес, gcc её инстанциирует (логично). Далее просто — функция упоминает статическую переменную (helper_).
В принципе тут можно пойти более коротким путём — параметризовать вспомогательный класс сразу адресом нужной статической переменной (helper_). Но я столкнулся с тем, что не все компиляторы хорошо это переваривают, хотя на некоторых gcc это работает как полагается.
В итоге это решение работает на msvc7.1, msvc8, msvc9, gcc3.4, gcc 4.1. На Comeau online — компилируется без ошибок, что радует.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: А зачем этов всё надо? (-)
От: Erop Россия  
Дата: 03.09.08 20:29
Оценка: +1
Здравствуйте, remark, Вы писали:

R>Вначале пример использования:


А зачем это всё надо?

Чем просто вызов статической функции явным образом хуже?

опять же, с порядклм вызова этих "статических конструкторов" соврешенно не понятно всё

Кстати, IMHO, тогда уж логичнее написать шаблон такого толка:
extern call_at_static_ctors<f_init> a;
extern call_at_static_dtors<f_deinit> b;
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: [Trick] Конструктор класса
От: Mazay Россия  
Дата: 04.09.08 02:26
Оценка:
Здравствуйте, remark, Вы писали:

R>Теперь реализация (для любителей хардкорного С++):

R>
R>template<typename derived_t, typename target_t = derived_t>
R>class class_initializer
R>{
.....
R>    static helper helper_;
.....
R>};
.....
R>template<typename derived_t, typename target_t>
R>typename class_initializer<derived_t, target_t>::helper 
R>    class_initializer<derived_t, target_t>::helper_;
.....
R>


Если я правильно понимаю ситуацию, то вся реализация лежит в заголовчном файле. Представим что этот заголовочник используется в статической библиотеке libMyLib.lib. В ней инстанцируется class_initializer<> для какого-нибудь типа Foo, объявленного и определённого в этой же библиотеке. А теперь главное не дать пользователю библиотеки самому инстанцировать class_initializer<Foo> и запретить ему включать заголовочник библиотеки, в котором объявлен class_initializer<Foo>. Иначе получим двойное опредление переменной class_initializer<Foo, Foo>::helper_.
Главное гармония ...
Re: А зачем это надо?
От: landerhigh Пират  
Дата: 04.09.08 04:38
Оценка:
Не могу придумать реальный пример использования.
www.blinnov.com
Re[2]: [Trick] Конструктор класса
От: remark Россия http://www.1024cores.net/
Дата: 04.09.08 07:23
Оценка:
Здравствуйте, Mazay, Вы писали:

M>Если я правильно понимаю ситуацию, то вся реализация лежит в заголовчном файле. Представим что этот заголовочник используется в статической библиотеке libMyLib.lib. В ней инстанцируется class_initializer<> для какого-нибудь типа Foo, объявленного и определённого в этой же библиотеке. А теперь главное не дать пользователю библиотеки самому инстанцировать class_initializer<Foo> и запретить ему включать заголовочник библиотеки, в котором объявлен class_initializer<Foo>. Иначе получим двойное опредление переменной class_initializer<Foo, Foo>::helper_.


Двойных определений статических переменных (так же как и функций) шаблонных классов не бывает. Они по-умолчанию работают так как будто они __declspec(selectany) (а фукнции так как будто они inline).


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Примеры использования
От: remark Россия http://www.1024cores.net/
Дата: 04.09.08 08:05
Оценка: 18 (2)
Могу привести как я это использую в библиотеке асинхронного агентного программирования.
Пользователь описывает свой класс агента следующим образом:
class my_agent : public zzz::agent<my_agent>
{
  // помимо вкусностей, которые нам даёт наследование и CRTP,
  // у агента есть функция статической подписки на сообщения.
  // Она вызывается из "конструктора базового класса" zzz::agent<>,
  // который сам делает некую работу по регистрации данного типа агента,
  // по инициализации необходимых структур данных, 
  // а потом вызывает пользовательскую функцию zzz_subscribe()
  static void zzz_subscribe()
  {
    subscribe<msg1>();
    subscribe<msg2>();
    subscribe<msg3>();
  }
};

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


Ну или классический пример, если в программе есть много типов чего-либо — ну там типов запросов на сервере, или типов команд в клиентском приложении, тогда описывать их можно следующим образом:
class request_123 : public request<request_123>
{
  // тут уже никаких дополнительных действий не надо,
  // т.к. наш базовый класс request<> сам в своём "статическом конструкторе"
  // зарегистрирует этот тип обработчика в фабрике обработчиков
};



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Вот если бы ещё и dll не было... (-)
От: Erop Россия  
Дата: 04.09.08 09:12
Оценка:
Здравствуйте, remark, Вы писали:

R>Двойных определений статических переменных (так же как и функций) шаблонных классов не бывает. Они по-умолчанию работают так как будто они __declspec(selectany) (а фукнции так как будто они inline).


R>


Вот если бы ещё и dll не было...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Примеры использования
От: Erop Россия  
Дата: 04.09.08 09:17
Оценка:
Здравствуйте, remark, Вы писали:

приложении, тогда описывать их можно следующим образом:
R>
R>  // тут уже никаких дополнительных действий не надо,
R>  // т.к. наш базовый класс request<> сам в своём "статическом конструкторе"
R>  // зарегистрирует этот тип обработчика в фабрике обработчиков
R>


R>


Супер! Так бы сразу и писал, что это всё проолжение темы о построении списка деклараций в CT. В CT похоже облом, и пришлось отступить до этапа статических конструкторов. В целом прикольно. Единственный существенный недостаток для пользователя -- труно врубиться когда и кто зовёт твою функцию ну да RTFM!
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Вот если бы ещё и dll не было... (-)
От: remark Россия http://www.1024cores.net/
Дата: 04.09.08 12:38
Оценка:
Здравствуйте, Erop, Вы писали:

R>>Двойных определений статических переменных (так же как и функций) шаблонных классов не бывает. Они по-умолчанию работают так как будто они __declspec(selectany) (а фукнции так как будто они inline).


E>Вот если бы ещё и dll не было...


Тут есть какие-то проблемы с dll'ями?


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: Вот если бы ещё и dll не было... (-)
От: Erop Россия  
Дата: 04.09.08 15:58
Оценка:
Здравствуйте, remark, Вы писали:

R>Тут есть какие-то проблемы с dll'ями?

Если я заведу клиентсякий класс, то в каждой dll в которую будет включаться его хедер добавится вызоывалка инициализации...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Вот если бы ещё и dll не было... (-)
От: remark Россия http://www.1024cores.net/
Дата: 04.09.08 16:07
Оценка:
Здравствуйте, Erop, Вы писали:

R>>Тут есть какие-то проблемы с dll'ями?


E>Если я заведу клиентсякий класс, то в каждой dll в которую будет включаться его хедер добавится вызоывалка инициализации...


По-моему, логично. А как классу жить без вызванного конструктора? А если ты включишь заголовок с определением объекта в несколько длл, в каждой длл тоже будет свой объект...


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[7]: Вот если бы ещё и dll не было... (-)
От: Erop Россия  
Дата: 04.09.08 16:11
Оценка: 3 (1)
Здравствуйте, remark, Вы писали:

R>По-моему, логично. А как классу жить без вызванного конструктора? А если ты включишь заголовок с определением объекта в несколько длл, в каждой длл тоже будет свой объект...


R>


Нифига не логично:
#ifdef MY_LIB
#define MY_LIB_SYMBOL __declspec(dllexport)
#else
#define MY_LIB_SYMBOL __declspec(dllimport)
#endif
MY_LIB_SYMBOL class MyClass : RemarksRegistrar<MyClass> {
    // ...
};
IMHO логично бы было, если бы инициализация вызывалась только в той dll которая экспортирует эту штуку...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: [Trick] Конструктор класса
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 04.09.08 16:21
Оценка: 9 (1)
Здравствуйте, remark

R>В итоге это решение работает на msvc7.1, msvc8, msvc9, gcc3.4, gcc 4.1. На Comeau online — компилируется без ошибок, что радует.


Алаверды с макросами, но без наследования
#include <iostream>

template<typename CLASS>
class class_initializer_t
{
public :
    struct helper_t
    {
        helper_t * trick_;
        helper_t()
            : trick_( &helper_ )
        {
            CLASS::static_ctor();
        }

        ~helper_t()
        {
            CLASS::static_dtor();
        }
    };

    static helper_t helper_;
};

template<typename CLASS>
typename class_initializer_t<CLASS>::helper_t 
    class_initializer_t<CLASS>::helper_;

#define DECL_INITIALIZER(CLASS) \
inline class_initializer_t<CLASS>::helper_t \
class_initializer__() const { \
  return class_initializer_t<CLASS>::helper_t(); \
}

struct A
{
    DECL_INITIALIZER(A)

    static void static_ctor()
    {
        std::cout << __FUNCTION__ << std::endl;
    }

    static void static_dtor()
    {
        std::cout << __FUNCTION__ << std::endl;
    }
};

struct B
{
    DECL_INITIALIZER(B)

    static void static_ctor()
    {
        std::cout << __FUNCTION__ << std::endl;
    }

    static void static_dtor()
    {
        std::cout << __FUNCTION__ << std::endl;
    }
};

int main()
{
    std::cout << __FUNCTION__ << std::endl;
}


Проверял с помощью msvc-7.1, msvc-8.0, gcc-3.4.5, gcc-4.1.2.


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[2]: [Trick] Конструктор класса
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 04.09.08 16:33
Оценка:
Здравствуйте, eao197, Вы писали:

E>Алаверды с макросами, но без наследования


Прошу воспринимать все как шутку, поскольку твой вариант умеет работать с шаблонными классами, а мой нет



SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: Пытался упростить - нашел bug in VC8 VC9 :)
От: Alexander G Украина  
Дата: 04.09.08 18:39
Оценка:
class foo;

template<int(foo::*)()>
struct bar;

class foo
{
  virtual int method();
  bar<&foo::method> foobar;
};
Русский военный корабль идёт ко дну!
Re: [Trick] Конструктор класса
От: sokel Россия  
Дата: 05.09.08 10:06
Оценка:
мда... если бы конструкторы классов инстанцировались, можно было бы проще сделать:

#include <stdio.h>

template<typename T>
struct static_init
{
    struct helper
    {
        void foo() {}
        helper    () { T::static_ctor(); }
        ~helper    () { T::static_dtor(); }
    };
    static_init() { init.foo(); } 
    static helper init;
};
template<typename T> typename static_init<T>::helper static_init<T>::init;

struct A : static_init<A>
{
    static void static_ctor() { printf("%s\n", __FUNCTION__); }
    static void static_dtor() { printf("%s\n", __FUNCTION__); }
};

struct B : static_init<B>
{
    static void static_ctor() { printf("%s\n", __FUNCTION__); }
    static void static_dtor() { printf("%s\n", __FUNCTION__); }
};

int main()
{
    A a; B b;
    printf("%s\n", __FUNCTION__);
}
Re[2]: [Trick] Конструктор класса
От: sokel Россия  
Дата: 05.09.08 10:11
Оценка:
Здравствуйте, sokel, Вы писали:

S>мда... если бы конструкторы классов инстанцировались, можно было бы проще сделать:

...
или так:

#include <stdio.h>

template<typename T>
struct static_init
{
    struct helper
    {
        inline void foo() {}
        helper    () { T::static_ctor(); }
        ~helper    () { T::static_dtor(); }
    };
    static_init() { init.foo(); } 
    static helper init;
};
template<typename T> typename static_init<T>::helper static_init<T>::init;

struct A : static_init<A>
{
    A() {}
    static void static_ctor() { printf("%s\n", __FUNCTION__); }
    static void static_dtor() { printf("%s\n", __FUNCTION__); }
};

struct B : static_init<B>
{
    B() {}
    static void static_ctor() { printf("%s\n", __FUNCTION__); }
    static void static_dtor() { printf("%s\n", __FUNCTION__); }
};

int main()
{
    printf("%s\n", __FUNCTION__);
}
Re[3]: [Trick] Конструктор класса
От: sokel Россия  
Дата: 05.09.08 12:45
Оценка:
Здравствуйте, eao197, Вы писали:

E>Прошу воспринимать все как шутку, поскольку твой вариант умеет работать с шаблонными классами, а мой нет


Это почему? Всё вроде будет работать. Макрос даже лучше, поскольку таблица виртуальных функций остается чистой.
Кстати, так ведь можно сократить?:

template<typename T>
struct static_init
{
    static_init()    { T::static_ctor(); }
    ~static_init()    { T::static_dtor(); }
    static static_init<T> init;
};
template<typename T> static_init<T> static_init<T>::init;

#define STATIC_INIT(T) static_init<T>& __static_init() { return static_init<T>::init; }
Re[4]: [Trick] Конструктор класса
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 05.09.08 12:55
Оценка:
Здравствуйте, sokel, Вы писали:

S>Кстати, так ведь можно сократить?:


S>
S>template<typename T>
S>struct static_init
S>{
S>    static_init()    { T::static_ctor(); }
S>    ~static_init()    { T::static_dtor(); }
S>    static static_init<T> init;
S>};
S>template<typename T> static_init<T> static_init<T>::init;

S>#define STATIC_INIT(T) static_init<T>& __static_init() { return static_init<T>::init; }
S>


Функция __static_init() для шаблонного класса никем не вызывается, поэтому и не инстанциируется. А значит и не инстанциируется static_init<T>::init.

Так что не работает.


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: [Trick] gcc - проверить перекрытие виртуального метода
От: Alexander G Украина  
Дата: 14.09.08 20:35
Оценка: 17 (1)
template<class D, class B, D d, B b>
struct override_check : class_initializer<override_check<D, B, d, b> >
{
  static void static_ctor()
  {
    assert(d==static_cast<D>(b));
    assert(typeid(d)!=typeid(b));
  }
  static void static_dtor()
  {
  }
};

#define OVERRIDE_CHECK__(A, B) A ## B
#define OVERRIDE_CHECK_(A, B) OVERRIDE_CHECK__(A, B)

#define OVERRIDE_CHECK(D, B) struct OVERRIDE_CHECK_(check, __LINE__) \
  : override_check<typeof(D), typeof(B), D, B> {};

struct S
{
  virtual int a() = 0;
  virtual int b();
};

struct D : S
{
  int a(); OVERRIDE_CHECK(&D::a, &S::a);
  virtual int b(); OVERRIDE_CHECK(&D::b, &S::b);
};
Русский военный корабль идёт ко дну!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.