Re[9]: Как реализовать RunTime регистрацию типа
От: Batiskaf Израиль http://www.mult.ru/
Дата: 15.11.02 08:34
Оценка:
Не хотелось бы быть назойливым, но все таки, может кто то сможет ответить на это, http://www.rsdn.ru/forum/Message.aspx?mid=131393&only=1
Автор: Batiskaf
Дата: 14.11.02
, особенно вторая часть месаги. Благодарности моей не будет границ, накликаю балов сколько хотите
Will I live tomorrow? Well I just can't say
But I know for sure — I don't live today.
Jimi Hendrix.
Re[6]: Как реализовать RunTime регистрацию типа
От: Vladik Россия  
Дата: 15.11.02 08:50
Оценка:
Здравствуйте, Batiskaf, Вы писали:

B>Предложенный вами метод применялся и в МФС и во многих других библиотеках, регистрация на вызове конструктора статик поля, и если я помню, поднимались на форуме проблемы с некоректной инициализацией статик полей, кажется кто-то упоминал что компилятор не гарантирует конструирования статик поля до вызова кода, который не обращается напрямую к этому полю, другими словами, класс еще не пропишется в карточке, а в коде будет уже обращение к карточке, где будет искаться функция-создатель по имени класса.


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

В каком-то .cpp есть функция получения статического объекта, который должен быть сконструирован ранше других.

TObject *GetStaticObject()
{
static TObject obj;
    return &obj;
}


Далее, к каждому .cpp подключается примерно такой .h файл:

class TStaticIniter
{
public:
    TStaticIniter(){GetStaticObject();}
};
static TStaticIniter static_object_initer;


При этом TObject будет создан гарантировано ранее других статических объектов, описанных после включения вышеприведенного хедера.

B>Также, при использовании такого метода регистрации совместно с компилятором Code Warior, возник совсем неожиданный трабл, компилятор категорически не хотел включать в исполняемый модуль объектник того класса, экземпляры которого явным образом не создаются в других объектниках, ведь функция создатель находится рядом с классом, в том же объектнике.


Здесь тоже без конкретного примера чего-то предложить сложно.Разве что сменить компилятор

B>Линкер не обнаруживал внешних ссылок на объектник, и тихонько его выкусывал. Может это и конфигурируется, незнаю. Как быть в таком случае. Так же я слышал что рассматривается предложение по приминению динамической загрузки С++ классов на момент необходимости ( понятия не имею как, кто бы просветил ), может вышеупомянутый трабл имеет непосредственное отношение к данному фичеру?


Вряд-ли И вообще "динамическая загрузка классов" в таком определении — нелепость какая-то. Может это про джаву было?
Как все запущенно...
Re[7]: Как реализовать RunTime регистрацию типа
От: Batiskaf Израиль http://www.mult.ru/
Дата: 15.11.02 11:57
Оценка:
Здравствуйте, Vladik, Вы писали:

V>Ну прриведи тогда конкретный пример, а то не очень понятно, что именно имеется ввиду. Вообще для решения проблемы создания некого статического объекта раньше других могу предложить примерно следующее.


Мне казалось, я все нормально объяснил, ну да ладно.
Тут речь не идет об очередности инициализации статик полей, а говорится о моменте инициализации статик поля вообще. Ведь все что сказано в определении языка относительно статик поля, что в момент ИСПОЛЬЗОВАНИЯ этого поля компилятором гарантируется, что оно будет прежде инициализированно. И никто не гарантирует инициализацию поля ( и запуск тем самым конструктора класса ), которое не используется в коде этого класса вообще, либо используется например в каком то методе ( не в единственном конструкторе класса ).
Попытаюсь выразить это в коде, код конечно гипотетический, но на мой взгляд есть вероятность того что он не будет работать как ожидалось на каком то гипотетическом компиляторе Х:


/**********************************************
        File A.h
**********************************************/

#include <iostream>
#include <vector>
#include <string>
using namespace std;

class A
{
public:
    A(const string& str);
    static bool HaveString(const string& str);
protected:
    typedef vector<string> str_vector;
    static str_vector& GetVector();
};




/**********************************************
        File A.cpp
**********************************************/

#include "A.h"
#include <algorithm>

A::A(const string& str)
{
    GetVector().push_back(str);
}
bool A::HaveString(const string& str)
{
    str_vector& v = GetVector();
    return find(v.begin(), v.end(), str) == v.end() ? false:true;
}

A::str_vector& A::GetVector()
{
    static str_vector tmp;
    return tmp;
}




/**********************************************
        File B.h
**********************************************/

#include "A.h"

class B
{
static A a;
public:
    void UseStaticField();
};




/**********************************************
        File B.cpp
**********************************************/

#include "B.h"

A B::a("class B");

void B::UseStaticField()
{
    size_t size = sizeof(a);
}




/**********************************************
        File main.cpp
**********************************************/

#include "A.h"
#include "B.h"
int main(void)
{
    const char* str = "class B";
    cerr<< "string \""<<str<< (A::HaveString(str)? "\" ":" not ")<<"available in list"<<endl;
    B().UseStaticField();
    cerr<< "string \""<<str<< (A::HaveString(str)? "\" ":" not ")<<"available in list"<<endl;
    return 0;
}


Так вот я о том, что результатом этой программы на мой взгляд не обязательно должен быть:
string "class B" available in list
string "class B" available in list


но может быть и так:
string "class B" not available in list
string "class B" available in list

Почему производитель компилятора не может делать такие припущения и не откладывать инициализацию статик поля на момент его использования, или не инициализировать поле вообще?

Что же касается CodeWarrior, мне кажется это продолжение той же темы:
#include "A.h"
int main(void)
{
    const char* str = "class B";
    cerr<< "string \""<<str<< (A::HaveString(str)? "\" ":" not ")<<"available in list"<<endl;
    return 0;
}


На класс В нет внешних ссылок ( то-есть инстанс класса В не создается в других объектных модулях ), что мешает производителям компиляторов исключать объектный модуль класса В? Что по-этим двум вопросам говорит стандарт?
Will I live tomorrow? Well I just can't say
But I know for sure — I don't live today.
Jimi Hendrix.
Re[6]: Как реализовать RunTime регистрацию типа
От: Анатолий Широков СССР  
Дата: 15.11.02 15:00
Оценка:
Спасибо за поднятую проблему. Вот ее решение:

// factory.h
...
///////////////////////////////////////////////////////////////////////////////
//    Вспомогательные макросы
//
#define DECLARE_FACTORY(D, T) \
    friend class Factory<##D, ##T>; 

//    полный аналог Init_Lib [3rd]
#define ENSURE_CREATE(D, T) \
    static Factory<##D, ##T> factory_of_##D(#D); 
...

// sample.h
#ifndef sample_h
#define sample_h

class Base
{
public:
    virtual ~Base() {}

    virtual void func() const = 0;
};

class Der1 : public Base
{
    Der1() {} 
public:
    void func() const
    {
        std::cout << "Der1::func\n";
    }

    DECLARE_FACTORY(Der1, Base)
};
ENSURE_CREATE(Der1, Base) 


class Der2 : public Base
{
    Der2() {} 
public:
    void func() const
    {
        std::cout << "Der2::func\n";
    }
    
    DECLARE_FACTORY(Der2, Base)
};
ENSURE_CREATE(Der2, Base)

#endif

// test.cpp
// пример использования
#include <iostream>
#include <memory>

#include "factory.h"
#include "sample.h"

// теперь factory_of_Der1, factory_of_Der2 попадает в каждую единицу трансляции
// поэтому теперь авторегистрация гарантируется:)
//
std::auto_ptr<Base> guard_der1(Class<Base>::instance().create("Der1"));
std::auto_ptr<Base> guard_der2(Class<Base>::instance().create("Der2"));

void test(Base *ptr)
{
    if( !ptr ) 
    {
        std::cout << "error: no allocated\n";
        return;
    }
    ptr->func();
}

int main(void)
{
    test(guard_der1.get());
    test(guard_der2.get());

    return 0;
}

Удачи.
Re[8]: Как реализовать RunTime регистрацию типа
От: Vladik Россия  
Дата: 15.11.02 17:41
Оценка:
Здравствуйте, Batiskaf, Вы писали:

B>Так вот я о том, что результатом этой программы на мой взгляд не обязательно должен быть:


Обязательно. Все "глобальные" статические переменные инициализируются до main().

[...]
B>На класс В нет внешних ссылок ( то-есть инстанс класса В не создается в других объектных модулях ), что мешает производителям компиляторов исключать объектный модуль класса В? Что по-этим двум вопросам говорит стандарт?

Стандарт не говорит, что объектный модуль и класс как-то связаны. А исключить соответсвующую функцию мешает взятие и использование ее адреса. Так что это проблема твоего компилятора.
Как все запущенно...
Re[6]: Как реализовать RunTime регистрацию типа
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 15.11.02 18:10
Оценка:
Здравствуйте, Batiskaf, Вы писали:

[...]

B>Также, при использовании такого метода регистрации совместно с компилятором Code Warior, возник совсем неожиданный трабл, компилятор категорически не хотел включать в исполняемый модуль объектник того класса, экземпляры которого явным образом не создаются в других объектниках, ведь функция создатель находится рядом с классом, в том же объектнике. Линкер не обнаруживал внешних ссылок на объектник, и тихонько его выкусывал. Может это и конфигурируется, незнаю. Как быть в таком случае.


Вообще — что-то загадочное. Попробуй а) объявить производящую функцию экспортируемой или б) сделать external-linkage функцию, в которую поместить вызов своей производящей функции (масло масляное, но уж шаманить — так шаманить).

Ну, например:


// MyMakeFunction() - производящая функция

extern "C" void _dummy(void) // подойдёт любой способ
{
  MyMakeFunction();
}


В таком случае линкер не имеет права удалять _dummy и, соответственно, MyMakeFunction со всеми последствиями не будет удалена тоже.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[9]: Как реализовать RunTime регистрацию типа
От: Анатолий Широков СССР  
Дата: 15.11.02 18:49
Оценка:
V>Обязательно.
В примере Batiskaf-а не обязательно:

// hdr.h
class A
{
public:
   static A a;
};

// hdr.cpp
A A::a;

// main.cpp
#include "hdr.h"

// так вот здесь никаких гарантий относительно того, что а будет инициализирована
// именно так была поставлена проблема.
A test = A::a;

int main()
{
   A b = A::a;
   return 0;
}
Re[10]: Как реализовать RunTime регистрацию типа
От: Vladik Россия  
Дата: 18.11.02 06:50
Оценка:
Здравствуйте, Анатолий Широков, Вы писали:

V>>Обязательно.

АШ>В примере Batiskaf-а не обязательно:

АШ>
АШ>// hdr.h
АШ>class A
АШ>{
АШ>public:

[ccode]
      A &getA()                
      {

АШ> static A a;
return a;

      }

АШ>};

АШ>// hdr.cpp

АШ>A A::a;

АШ>// main.cpp

АШ>#include "hdr.h"

АШ>// так вот здесь никаких гарантий относительно того, что а будет инициализирована

АШ>// именно так была поставлена проблема.
АШ>A test = A::a;

A test = A::getA();


Тогда вот это должно решить проблему.
Как все запущенно...
Re[7]: Как реализовать RunTime регистрацию типа
От: Batiskaf Израиль http://www.mult.ru/
Дата: 18.11.02 08:18
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:


ГВ>Вообще — что-то загадочное. Попробуй а) объявить производящую функцию экспортируемой или б) сделать external-linkage функцию, в которую поместить вызов своей производящей функции (масло масляное, но уж шаманить — так шаманить).


ГВ>Ну, например:


ГВ>

ГВ>// MyMakeFunction() - производящая функция

ГВ>extern "C" void _dummy(void) // подойдёт любой способ
ГВ>{
ГВ>  MyMakeFunction();
ГВ>}

ГВ>


ГВ>В таком случае линкер не имеет права удалять _dummy и, соответственно, MyMakeFunction со всеми последствиями не будет удалена тоже.


Шаманил я уже по-всякому, и в барабаны бил, и в трубы дудел, не сдается Code Warrior 7 for Windows. Код такой:


/**********************************************         
    File A.h 
**********************************************/  
#ifndef A_H
#define A_H
#pragma warning(disable: 4786)
#include <iostream> 
#include <map> 
#include <string> 
using namespace std;  

typedef void* (__cdecl *creator_t) ();

class A 
{ 

public:     
    A(const string& str, creator_t fCrt);     
    static void GetInstanceByName(const string& str); 
protected:     
    typedef map<string, creator_t> type_map;     
    static type_map& GetMap(); 
};    

#endif



/**********************************************         
    File A.cpp 
**********************************************/  
#include <algorithm>  
#include "A.h" 

A::A(const string& str, creator_t fCrt) 
{     
    GetMap()[str] = fCrt;
}

void A::GetInstanceByName(const string& str) 
{     
    type_map& v = GetMap();     
    if(v.find(str) != v.end())
        v[str]();
} 

A::type_map& A::GetMap() 
{     
    static type_map tmp;     
    return tmp; 
}




/**********************************************         
    File B.h 
**********************************************/  
#ifndef B_H
#define B_H

#include "A.h"  

class B 
{ 
    public:     
    B();
    protected:
    /*    static A a;
        static B* CreateB();*/
};    

#endif



/**********************************************         
    File B.cpp 
**********************************************/  
#include "B.h"  
// множество перепробованных комбинаций  :) 
extern B* CreateB()
{
    return new B();
}

extern A a("class B", (creator_t) CreateB);  

/*extern "C" B* CreateB()
{
    return new B();
}

extern "C" A a("class B", (creator_t) CreateB);  
*/

/*B* B::CreateB()
{
    return new B();
}

A B::a("class B", (creator_t) B::CreateB);
*/

B::B()
{
    cerr<<"B created"<<endl;
}



/**********************************************         
    File main.cpp 
**********************************************/  
#include "A.h" 
//#include "B.h" 

int main(void) 
{     
    const char* str = "class B";     
        //B b;
    A::GetInstanceByName(str);// не проходит B::B() пока не разкоментирую предидущую строку, тогда два раза конструктор вызывается и конструктор класса А тоже проходит
    return 0; 
}


И все это безобразие происходит когда классы А и В во внешней либ находятся и к маин ехе прилинковываются, а когда собираю все классы в рамках одного ехе тогда все нормально, тогда и статик поля классов нормально работают...
Will I live tomorrow? Well I just can't say
But I know for sure — I don't live today.
Jimi Hendrix.
Re[8]: Как реализовать RunTime регистрацию типа
От: Анатолий Широков СССР  
Дата: 18.11.02 08:24
Оценка:
А вот это объясни:
extern A a("class B", (creator_t) CreateB);
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.