[c++] порядок создания классов-синглтонов
В общем случае, порядок инициализации статических мемберов классов и глобальных переменных не определен. Единственное, что может помочь - статические переменные в функциях и соответственно определенный порядок их вызова.
А вообще, этот код - полный пиздец. В качестве рекомендации, а не для стеба, советую почитать что-нибудь в духе Макконела, совершенный код. Или подобное чтиво, где описаны приемы, как не отстрелить палкой себе яйца.
А вообще, этот код - полный пиздец. В качестве рекомендации, а не для стеба, советую почитать что-нибудь в духе Макконела, совершенный код. Или подобное чтиво, где описаны приемы, как не отстрелить палкой себе яйца.
а можно более развернуто - в чем именно пиздец? в идее так делать синглтоны? регать их из одного класса в другом в конструкторе?
A и B у тебя связаны сильно, да еще и друг у друга френды. Синглтон не безпроблемный объект. А когда ты их два сделал, да еще и связал вместе, то это бомба замедленного действия: ты сейчас свои проблемы решишь как-нибудь, а потом надо будет что-нибудь добавить и единственное полезное, что можно будет сделать с кодом - выкинуть его.
Сходу могу предложить сделать фабрику классов B, которая заодно и регать будет их у A. Экземпляр A (указатель или ссылку будешь подавать в конструкторе фабрики, поэтому гарантированно A будет создаваться раньше B.
Хотя встанет вопрос, где будет жить фабрика... ну в таком случае и A туда запихнуть и пусть фабрика живет синглтоном.
Сходу могу предложить сделать фабрику классов B, которая заодно и регать будет их у A. Экземпляр A (указатель или ссылку будешь подавать в конструкторе фабрики, поэтому гарантированно A будет создаваться раньше B.
Хотя встанет вопрос, где будет жить фабрика... ну в таком случае и A туда запихнуть и пусть фабрика живет синглтоном.
B - это не класс, а шаблон, когда нужен экземпляр B, его нужно от шаблона получить, и он должен быть синглтоном, потому что в нем идеологически необходимы 2 статические функции, каждая из которых должна иметь доступ к экземпляру своего класса. как я сделаю фабрику шаблонов?
вообще это задача такая. я ничего более умного не придумал. A - это объект который регистрирует службы Windows, а B - это собственно сама служба. Сделать ее простым наследованием без шаблонов можно, но придется сделать грязый хак - передачу ссылки на экземпляр в потоке кода с залезанием на низкий уровень и вставкой кода в виде шестнадцатиричных байтов в скомпилированном виде с подстановкой куда-то указателя.
вообще это задача такая. я ничего более умного не придумал. A - это объект который регистрирует службы Windows, а B - это собственно сама служба. Сделать ее простым наследованием без шаблонов можно, но придется сделать грязый хак - передачу ссылки на экземпляр в потоке кода с залезанием на низкий уровень и вставкой кода в виде шестнадцатиричных байтов в скомпилированном виде с подстановкой куда-то указателя.
Шаблонный класс - это тоже класс.
В фабрике сделай шаблонный метод, содержащий статическую переменную типа B<T> и возвращай ссылку или указатель на него, перед этим регистрируя в экземпляре A. Если экземпляр A у тебя будет объявлен статическим мембером, то он гарантированно будет инстанцироваться раньше всех B<T>. Btw: в стандарте есть какие-то фишки на тему отложенной инициализациии статиков, но я их уже не помню и по-идее, они не должны тут проявиться.
Хотя если фабрика будет синглтоном, то A делать статиком не обязательно, просто создать в конструкторе.
В фабрике сделай шаблонный метод, содержащий статическую переменную типа B<T> и возвращай ссылку или указатель на него, перед этим регистрируя в экземпляре A. Если экземпляр A у тебя будет объявлен статическим мембером, то он гарантированно будет инстанцироваться раньше всех B<T>. Btw: в стандарте есть какие-то фишки на тему отложенной инициализациии статиков, но я их уже не помню и по-идее, они не должны тут проявиться.
Хотя если фабрика будет синглтоном, то A делать статиком не обязательно, просто создать в конструкторе.
спасибо за подсказку, вроде бы понял твою мысль, попробую реализовать.
еще пришло в голову что можно список объектов перенести статиком в AbstractB и в нем регистрироваться, а A останется френдом и будет оттуда список забирать когда ему надо, вроде бы это тоже должно решить проблему с порядком создания.
еще пришло в голову что можно список объектов перенести статиком в AbstractB и в нем регистрироваться, а A останется френдом и будет оттуда список забирать когда ему надо, вроде бы это тоже должно решить проблему с порядком создания.
Нет, не поможет. Ты фактически возвращаешься к своему первому посту, только в другом виде. Если ты определяешь статические мемберы классов или глобальные переменные, то тебе нельзя закладываться на порядок их инициализации. Единственное, на что можно рассчитывать, так это если ты одну переменную инициализируешь компайл тайм константой, а вторую ты вычисляешь вызовом функции. Тогда первая будет проинициализирована сначала. Опять же, за подробностями к стандарту.
И насчет френдов, найди все причины, по которым ты решил добавить френдов и избавься от них(причин и френдов). Я думаю, должно получиться. При хорошем проектировании классов, в подавляющем большинстве случаев френды не нужны.
И насчет френдов, найди все причины, по которым ты решил добавить френдов и избавься от них(причин и френдов). Я думаю, должно получиться. При хорошем проектировании классов, в подавляющем большинстве случаев френды не нужны.
а разве статик мембер предка не проинициализируется гарантированно раньше мемберов потомка, в частности к моменту вызова конструктора потомка?
насчет френдов - без них можно обойтись, если добавить в явном виде функцию регистрации класса B в A в паблик, но она во внешнем интерфейсе строго говоря не нужна и должна быть спрятана. Тут вопрос что хуже - выставлять явно внутренние методы в паблик или френдить 2 класса. Либо еще наверно можно пронаследоваться от общего предка с защищенной виртуальной функцией, но в одном из классов эта функция окажется лишней, унаследуется, так сказать в нагрузку.
насчет френдов - без них можно обойтись, если добавить в явном виде функцию регистрации класса B в A в паблик, но она во внешнем интерфейсе строго говоря не нужна и должна быть спрятана. Тут вопрос что хуже - выставлять явно внутренние методы в паблик или френдить 2 класса. Либо еще наверно можно пронаследоваться от общего предка с защищенной виртуальной функцией, но в одном из классов эта функция окажется лишней, унаследуется, так сказать в нагрузку.
Нет, наследование здесь ни при чем. Запись static blabla; в классе - это декларейшн. Дефинишн будет за пределами класса. И там уже предок, потомок, не имеет значения. Порядок для предков/потомков имеет значение только для нестатических мемберов во время инстанцирования самого класса, а не создания каких-то там статических мемберов.
> A - это объект который регистрирует службы Windows
Назначение класса как раз говорит о том, что функция регистрация должна быть публичной.
Зачем, кстати, класс AbstactB объявлен френдом в A?
> A - это объект который регистрирует службы Windows
Назначение класса как раз говорит о том, что функция регистрация должна быть публичной.
Зачем, кстати, класс AbstactB объявлен френдом в A?
у AbstractB есть защищенные виртуальные функции
typedef void (__stdcall *winServiceProcTypeDWORD, char**); // указатель на функцию службы Windows
virtual winServiceProcType getServiceProc = 0; // возвращает указатель на статик функцию данной инстанции шаблона
virtual char* getCharServiceName = 0;
которые определяются в шаблоне-потомке.
class A вызывает их в своем публичном методе RegisterServices в рантайме когда все шаблоны проинстанциированы и добавлены в его список.
а вот метод B<T>.RegisterService (это который сейчас описан как AddB в предке) вызывать руками из рантайма не хочется, хочется чтоб он был спрятан и вызывался автоматически. потому они друг другу и френды.
typedef void (__stdcall *winServiceProcTypeDWORD, char**); // указатель на функцию службы Windows
virtual winServiceProcType getServiceProc = 0; // возвращает указатель на статик функцию данной инстанции шаблона
virtual char* getCharServiceName = 0;
которые определяются в шаблоне-потомке.
class A вызывает их в своем публичном методе RegisterServices в рантайме когда все шаблоны проинстанциированы и добавлены в его список.
а вот метод B<T>.RegisterService (это который сейчас описан как AddB в предке) вызывать руками из рантайма не хочется, хочется чтоб он был спрятан и вызывался автоматически. потому они друг другу и френды.
Я щас мало что смогу придумать, надо порисовать квадратики, но могу предложить тебе вот что. Отнаследуйся от B<T>, пусть это будет, например RegB<T>. И в этом классе сделай публичный метод регистрации. В фабрике создавай именно его, RegB<T>, а наружу возвращай только B<T>. И RegB<T> передавай только в A, чтобы тот смог вызывать RegisterService.
Ну и в качестве хинта, создавай не сами объекты, а шаред птр на объект. Указатель можно удалить, а ссылку можно забыть поставить, что приведет к двум удалениям объекта. А шаред птр втоде как понадежнее.
Ну и в качестве хинта, создавай не сами объекты, а шаред птр на объект. Указатель можно удалить, а ссылку можно забыть поставить, что приведет к двум удалениям объекта. А шаред птр втоде как понадежнее.
Дефинишн будет за пределами классаи если в одной единице трансляции, то инициализация будет в гарантированном порядке происходить.
хотя это опасное знание, лучше его не распространять 

гарантированном порядке происходитьАга. Только порядок будет известен после компиляции.
Ага. Только порядок будет известен после компиляции.не мути воду, порядок инициализации в одной единице трансляции строго определен стандартом
3.6.2 Initialization of non-local objects [basic.start.init]
1 Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization
takes place. Zero-initialization and initialization with a constant expression are collectively called static
initialization; all other initialization is dynamic initialization. Objects of POD types (3.9) with static storage
duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization
takes place. Objects with static storage duration defined in namespace scope in the same translation
unit and dynamically initialized shall be initialized in the order in which their definition appears in the
translation unit. [Note: 8.5.1 describes the order in which aggregate members are initialized. The initialization
of local static objects is described in 6.7. ]
3.7.1 Static storage duration [basic.stc.static]
1 All objects which neither have dynamic storage duration nor are local have static storage duration. The
storage for these objects shall last for the duration of the program (3.6.2, 3.6.3).
Извините, никогда не закладывался на порядок инициализации нелокальных переменных 

люблю я такие треды, где про c++, синглтоны, шаблоны и еще что-то. я совершенно не понимаю о чем здесь написано, и даже в том коде, который тут приведён я не смог разобраться. от этого обсуждаемая тема выглядет более значимой и даже где-то местами поддувает легкий сквознячок элитности. до самого последнего поста не покидает ощущение, что предмет обсуждения как-то связан ни то с hi-tech'ом послезавтрашнего дня ни то с филосовскими знаниями атлантов. но ради всего сущего, объясните мне уже кто-нибудь:
О ЧЕМ ЭТОТ ТРЕД И ГДЕ ЭТО ПРИМЕНЯЕТСЯ?
О ЧЕМ ЭТОТ ТРЕД И ГДЕ ЭТО ПРИМЕНЯЕТСЯ?
все очень просто. я пытаюсь написать классовую обертку для создания сервисов Windows. на первый взгляд это очень простая и тривиальная задача, пока не начнешь её реализовывать. при более внимательном рассмотрении оказывается что не так просто сделать класс, который бы позволял создавать более 1 службы не повторяя код и не используя чисто процедурный подход. видимо, не очень удачный дизайн этой подсистемы дает себя знать. ну и еще у меня ограничение - не использовать сторонние тяжеловесные библиотеки, ибо служба нужна в программе как альтернатива демонизации под линуксом и солярисом. там кстати код отвечающий за это занимает десяток строк.
BULLSHIT!
using namespace std;Так пишут только пидорасы.
Если тебя задалбывает писать в определённом cpp'нике std::string, то можно написать using std::string. Если уж совсем-совсем, хотя я не знаю такой ситуации, можно написать using namespace, но не в h-файле же? Ты же тем самым уничтожаешь всё то, для чего был создан неймспес!
По теме: синглтоны пишут только пидорасы. Откуда эта тяга за меня решать сколько экземпляров класса создавать?
По теме: private пишут только пидорасы. Откуда эта тяга за меня решать, какие методы мне можно вызывать, а какие нет?
Есть и такая точка зрения, уверяю. Бачан, например, считает что писать прайветы — не круто.
Тем не менее разница просто принципиальная. Приматное поле или приватный метод — это то, что ты хочешь спрятать, чаще всего это РЕАЛИЗАЦИЯ. Конечно же, глупо делать поле приватным и потом давать два метода set и get, так делают пидорасы. Но если там что-то происходит ещё, то это нормально.
А вот синглтоны — пидерастия в чистом виде. Почему я не могу создать ДВА экземпляра класса если я это хочу? Это может быть с чем угодно связано.
Тем не менее разница просто принципиальная. Приматное поле или приватный метод — это то, что ты хочешь спрятать, чаще всего это РЕАЛИЗАЦИЯ. Конечно же, глупо делать поле приватным и потом давать два метода set и get, так делают пидорасы. Но если там что-то происходит ещё, то это нормально.
А вот синглтоны — пидерастия в чистом виде. Почему я не могу создать ДВА экземпляра класса если я это хочу? Это может быть с чем угодно связано.
BULLSHIT!Опередил, мерзавец!

Смотри, кстати, хомячок люто и бешенно минусуют. Вирус острого синдрома ООП прямо...
я предполагаю, что у тебя больше опыта, чем у меня. предложи решение обозначенной проблемы без синглтонов и прочего.
в идеале хочется примерно следующего:
надеюсь, примеры того какой API нужен для регистрации сервиса и какие функции нужно им передать писать не нужно.
в идеале хочется примерно следующего:
class MyService:public WinService
{
void OnStart; // перекрытая виртуальная
void OnStop; // перекрытая виртуальная
}
void OnStart
{
// Здесь десериализуем какие-то данные, начинаем слушать TCP порт, принимаем подключения, запускаем рабочий цикл службы в котором делаем что-то полезное.
LOG_BEGIN(EVENT_LOG|1);
LOG("Service started (name)");
LOG(this->serviceName);
LOG_END;
}
void OnStop
{
//Здесь завершаем потоки службы, останавливаем TCP сервер, сериализуем данные.
LOG_BEGIN(EVENT_LOG|1);
LOG("Service stopped (name)");
LOG(this->serviceName);
LOG_END;
}
int main(int ac, char ** av)
{
MyService svc1,svc2;
svc1.setName("name1");
svc2.setName("name2");
SCM scm; // service control manager
scm.add(svc1);
scm.add(svc2);
scm.registerServices;
return 0;
}
надеюсь, примеры того какой API нужен для регистрации сервиса и какие функции нужно им передать писать не нужно.
void OnStart; // перекрытая виртуальнаянахуа писать эти комментарии? потому что лень написать virtual, ведь это не обязательно, а без него — непонятно, поэтому комментарии?
void OnStop; // перекрытая виртуальная
потому что при перекрытии не нужно писать virtual
это кто сказал?
Есть и такая точка зрения, уверяю. Бачан, например, считает что писать прайветы — не круто.Бачан считает, что писать class не круто, мы не об этом сейчас.
Тем не менее разница просто принципиальная. Приматное поле или приватный метод — это то, что ты хочешь спрятать, чаще всего это РЕАЛИЗАЦИЯ.
Конечно же, глупо делать поле приватным и потом давать два метода set и get, так делают пидорасы. Но если там что-то происходит ещё, то это нормально.другого выхода нет, иначе получается неподдерживаемая шняга, в том-то и дело, что поле — это РЕАЛИЗАЦИЯ set/get, т.е. свойства. Она-то и может измениться. А если все настолько ясно, что меняться не будет, хуячь структуру.
А вот синглтоны — пидерастия в чистом виде. Почему я не могу создать ДВА экземпляра класса если я это хочу? Это может быть с чем угодно связано.Почему я не могу заиметь доступ (кстати, я — могу
) к приватным данным, это может быть с чем угодно связано.Не, синглтоны — уебищно, но ты причин этому не привел. Хотелки — это не аргумент.
предложи решение обозначенной проблемы без синглтонов и прочего.Ну так и напиши именно это... Что ты там хочешь делать синглтоном?
в идеале хочется примерно следующего:
OnStart должна вызываться из функции, условно назовем ее serviceProc, которая регистрируется в винде, вызывается виндой и содержит параметры типа int и char**, уговорить винду передать мне в этих параметрах this чтобы вызвать OnStart я не могу, serviceProc - статическая, ей this конечно же не передается. Передавать this в глобальной переменной - а как тогда создавать больше 1 экземпляра класса? отсюда синглтон, он лучше чем глобальная переменная имхо. а чтобы можно было более 1 службы делать - делаем шаблон этого синглтона и инстанцируем от какого-нибудь класса - не принципиально какого, но можно наверно даже на него что-то полезное навесить.
блиа, да тут повторяется ситуация с тем комментом, зачем-то впечатался синглтон (не пишем virtual а чтобы из этого выпутаться, создается шаблон (коммент) 
Кстате, если уж на то пошло, то синглтон включает две составляющих: глобальный доступ и ограничение инстанцирования. Не обязательно для получения глобального доступа ограничивать инстанцирование, либо наоборот.

Кстате, если уж на то пошло, то синглтон включает две составляющих: глобальный доступ и ограничение инстанцирования. Не обязательно для получения глобального доступа ограничивать инстанцирование, либо наоборот.
я предполагаю, что у тебя больше опыта, чем у меня. предложи решение обозначенной проблемы без синглтонов и прочего.А нужно ли там вообще делать класс?
Ты tcp-сервер пишешь? Я вообще не понимаю зачем заворачивать в класс то, что создаётся в единственном экземпляре. Хотя сам иногда заворачиваю, но делаю просто методы start/stop и прочие.
Ну т. е. ты опиши задачу, и попытайся сделать максимально просто. Не пытайся применить весь комплекс знаний которые у тебя есть, простое решение найди, оно же будет работать. А что тебе ещё надо от программы?
Ну во-первых мне нужно все-таки 2 службы для двух таргетов одного проекта - клиент и сервер, хочется использовать общий код. Класс я начал делать чтобы не писать один код 2 раза и потому, что не думал, что это окажется таким адским геморроем, думал там не сложнее чем, к примеру тред в класс завернуть. Ну и задача сама по себе довольно интересная в том плане, чтобы найти красивое решение. В конечном итоге я нашел варианты решения - 1) поменять порядок объектных файлов в мейкфайле, это поменяет порядок инициализации статик членов на нужный (фиговый компайлеро-зависимый вариант); 2) отказаться от регистрации класса в конструкторе, перенеся это в getInstance который проверяет флаг зарегистрированности экземпляра, установленный в false в конструкторе - кривовато, но не зависимо от компайлера. 3) не делать статик экземпляры, а делать статик указатели на них и создавать на куче - не нравится, потому что не понятно что делать в конструкторе, если память не выделилась - код возвращать некуда, а кидание эксепшенов в этом проекте не одобряется. С фабриками делать не стал, мне кажется это еще сильнее усложнит эту часть программы, которую изначально надеялся решить вообще одним классом.
мне нужно все-таки 2 службы для двух таргетов одного проектаМне кажется, что ты с терминологией перегнул. Я вот не понимаю что такое "два таргета одного проекта". И что такое "служба"?
Класс я начал делать чтобы не писать один код 2 раза
Не бойся, пиши 10 раз, это не страшно. На одиннадцатый придёт какое-то решение, которое будет на голову выше остальных. Красивый код рождается не в муках поиска, а сам собой во время практики.
Ну и вообще, ты так и не описал задачу. У тебя классы, фабрики, службы... что хоть делаешь? Подыми голову, посмотри вокруг.
делать статик указатели на них и создавать на куче - не нравится, потому что не понятно что делать в конструкторе, если память не выделилась - код возвращать некуда, а кидание эксепшенов в этом проекте не одобряется.госпадикакойбред...
2 таргета - это две цели сборки, два результирующих экзешника. один из экзешников это сервер, другой - клиент, оба должны запускаться в фоновом режиме не зависимо от того сделал ли пользователь windows logon. У них большАя часть кода общая, но не вся, соответственно 2 ф-и main в проекте, target - это как оно называется в kdevelop.
Служба - это service win32, та для которой можно делать net start и net stop.
Служба - это service win32, та для которой можно делать net start и net stop.
госпадикакойбредконтрнебред в студию. а то теоретически критиковать каждый горазд.
не понятно что делать в конструкторе, если память не выделилась - код возвращать некуда, а кидание эксепшенов в этом проекте не одобряетсяохеть! Действительно непонятно! А знаете, почему? Потому что исключения как раз и созданы, чтобы их кидать из конструктора! Не хотите исключений — не используйте

ладно, тут надо помочь с проблемой, а все начали обсирать
Простите 
Простите 
не кидать исключения - не мое решение.
с проблемой можно не помогать, работающее решение таки есть.
теперь хочется красивое решение, но это уже опционально и не к спеху.
теперь хочется красивое решение, но это уже опционально и не к спеху.
хотя нет, я уже предложил подход: надо решить, зачем синглтоны тут нужны? Для глобальной точки доступа или для ограничения количества? Теоретически вот нафига ограничивать количество? Так ли это обязательно? Попробуй от этого отказаться (в надежде, что никто и не будет пытаться создавать второй экземпляр а потом и от глобальности. И нормально В main сконфигурируй эти свои классы, а дальше уж как-нибудь их пропихни куда надо... Не все ж синглтонами писать...
а, т.е. уже можно обсирать? 

ага, только конструктивно, а не в стиле "ООП используют одни пидарасы"
не кидать исключения - не мое решение.т.е. если в конструкторе проблема — флаг какой-то выставляется, или что?
нет, по возможности избегается код в конструкторе, который может вызвать проблемы.
Ну да, делайте тогда методы Init...
Давай разбираться. Это означает, что память в конструкторе на стеке выделяется?
примерно так и делается. но имхо это не красиво.
да, на стеке. или в сегменте данных.
Оставить комментарий
elenangel
возникла такая задачка. есть класс A и шаблон B, объявленный как потомок AbstractB. оба синглтоны. при инстанциировании шаблона в конструкторе он добавляет свой this в список класса A. объявлено это дело таким образом:так вот, изначально в классе A тоже его единственный экземпляр был объявлен не как указатель, а как сам класс, но получалось что к моменту инстанциирования шаблона он еще не создан, а конструктор B уже пытается в его список указатель на себя добавить.
можно ли как-то заставить с++ вначале создавать синглтон A, и только после него создавать инстансы B<>?