[c++] порядок создания классов-синглтонов
А вообще, этот код - полный пиздец. В качестве рекомендации, а не для стеба, советую почитать что-нибудь в духе Макконела, совершенный код. Или подобное чтиво, где описаны приемы, как не отстрелить палкой себе яйца.
а можно более развернуто - в чем именно пиздец? в идее так делать синглтоны? регать их из одного класса в другом в конструкторе?
Сходу могу предложить сделать фабрику классов B, которая заодно и регать будет их у A. Экземпляр A (указатель или ссылку будешь подавать в конструкторе фабрики, поэтому гарантированно A будет создаваться раньше B.
Хотя встанет вопрос, где будет жить фабрика... ну в таком случае и A туда запихнуть и пусть фабрика живет синглтоном.
вообще это задача такая. я ничего более умного не придумал. A - это объект который регистрирует службы Windows, а B - это собственно сама служба. Сделать ее простым наследованием без шаблонов можно, но придется сделать грязый хак - передачу ссылки на экземпляр в потоке кода с залезанием на низкий уровень и вставкой кода в виде шестнадцатиричных байтов в скомпилированном виде с подстановкой куда-то указателя.
В фабрике сделай шаблонный метод, содержащий статическую переменную типа B<T> и возвращай ссылку или указатель на него, перед этим регистрируя в экземпляре A. Если экземпляр A у тебя будет объявлен статическим мембером, то он гарантированно будет инстанцироваться раньше всех B<T>. Btw: в стандарте есть какие-то фишки на тему отложенной инициализациии статиков, но я их уже не помню и по-идее, они не должны тут проявиться.
Хотя если фабрика будет синглтоном, то A делать статиком не обязательно, просто создать в конструкторе.
еще пришло в голову что можно список объектов перенести статиком в AbstractB и в нем регистрироваться, а A останется френдом и будет оттуда список забирать когда ему надо, вроде бы это тоже должно решить проблему с порядком создания.
И насчет френдов, найди все причины, по которым ты решил добавить френдов и избавься от них(причин и френдов). Я думаю, должно получиться. При хорошем проектировании классов, в подавляющем большинстве случаев френды не нужны.
насчет френдов - без них можно обойтись, если добавить в явном виде функцию регистрации класса B в A в паблик, но она во внешнем интерфейсе строго говоря не нужна и должна быть спрятана. Тут вопрос что хуже - выставлять явно внутренние методы в паблик или френдить 2 класса. Либо еще наверно можно пронаследоваться от общего предка с защищенной виртуальной функцией, но в одном из классов эта функция окажется лишней, унаследуется, так сказать в нагрузку.
> A - это объект который регистрирует службы Windows
Назначение класса как раз говорит о том, что функция регистрация должна быть публичной.
Зачем, кстати, класс AbstactB объявлен френдом в A?
typedef void (__stdcall *winServiceProcTypeDWORD, char**); // указатель на функцию службы Windows
virtual winServiceProcType getServiceProc = 0; // возвращает указатель на статик функцию данной инстанции шаблона
virtual char* getCharServiceName = 0;
которые определяются в шаблоне-потомке.
class A вызывает их в своем публичном методе RegisterServices в рантайме когда все шаблоны проинстанциированы и добавлены в его список.
а вот метод B<T>.RegisterService (это который сейчас описан как AddB в предке) вызывать руками из рантайма не хочется, хочется чтоб он был спрятан и вызывался автоматически. потому они друг другу и френды.
Ну и в качестве хинта, создавай не сами объекты, а шаред птр на объект. Указатель можно удалить, а ссылку можно забыть поставить, что приведет к двум удалениям объекта. А шаред птр втоде как понадежнее.
Дефинишн будет за пределами классаи если в одной единице трансляции, то инициализация будет в гарантированном порядке происходить.
хотя это опасное знание, лучше его не распространять
гарантированном порядке происходитьАга. Только порядок будет известен после компиляции.
Ага. Только порядок будет известен после компиляции.не мути воду, порядок инициализации в одной единице трансляции строго определен стандартом
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).
Извините, никогда не закладывался на порядок инициализации нелокальных переменных
О ЧЕМ ЭТОТ ТРЕД И ГДЕ ЭТО ПРИМЕНЯЕТСЯ?
все очень просто. я пытаюсь написать классовую обертку для создания сервисов Windows. на первый взгляд это очень простая и тривиальная задача, пока не начнешь её реализовывать. при более внимательном рассмотрении оказывается что не так просто сделать класс, который бы позволял создавать более 1 службы не повторяя код и не используя чисто процедурный подход. видимо, не очень удачный дизайн этой подсистемы дает себя знать. ну и еще у меня ограничение - не использовать сторонние тяжеловесные библиотеки, ибо служба нужна в программе как альтернатива демонизации под линуксом и солярисом. там кстати код отвечающий за это занимает десяток строк.
BULLSHIT!
using namespace std;Так пишут только пидорасы.
Если тебя задалбывает писать в определённом cpp'нике std::string, то можно написать using std::string. Если уж совсем-совсем, хотя я не знаю такой ситуации, можно написать using namespace, но не в h-файле же? Ты же тем самым уничтожаешь всё то, для чего был создан неймспес!
По теме: синглтоны пишут только пидорасы. Откуда эта тяга за меня решать сколько экземпляров класса создавать?
По теме: private пишут только пидорасы. Откуда эта тяга за меня решать, какие методы мне можно вызывать, а какие нет?
Тем не менее разница просто принципиальная. Приматное поле или приватный метод — это то, что ты хочешь спрятать, чаще всего это РЕАЛИЗАЦИЯ. Конечно же, глупо делать поле приватным и потом давать два метода set и get, так делают пидорасы. Но если там что-то происходит ещё, то это нормально.
А вот синглтоны — пидерастия в чистом виде. Почему я не могу создать ДВА экземпляра класса если я это хочу? Это может быть с чем угодно связано.
BULLSHIT!Опередил, мерзавец!
Смотри, кстати, хомячок люто и бешенно минусуют. Вирус острого синдрома ООП прямо...
в идеале хочется примерно следующего:
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 службы делать - делаем шаблон этого синглтона и инстанцируем от какого-нибудь класса - не принципиально какого, но можно наверно даже на него что-то полезное навесить.
Кстате, если уж на то пошло, то синглтон включает две составляющих: глобальный доступ и ограничение инстанцирования. Не обязательно для получения глобального доступа ограничивать инстанцирование, либо наоборот.
я предполагаю, что у тебя больше опыта, чем у меня. предложи решение обозначенной проблемы без синглтонов и прочего.А нужно ли там вообще делать класс?
Ты tcp-сервер пишешь? Я вообще не понимаю зачем заворачивать в класс то, что создаётся в единственном экземпляре. Хотя сам иногда заворачиваю, но делаю просто методы start/stop и прочие.
Ну т. е. ты опиши задачу, и попытайся сделать максимально просто. Не пытайся применить весь комплекс знаний которые у тебя есть, простое решение найди, оно же будет работать. А что тебе ещё надо от программы?
Ну во-первых мне нужно все-таки 2 службы для двух таргетов одного проекта - клиент и сервер, хочется использовать общий код. Класс я начал делать чтобы не писать один код 2 раза и потому, что не думал, что это окажется таким адским геморроем, думал там не сложнее чем, к примеру тред в класс завернуть. Ну и задача сама по себе довольно интересная в том плане, чтобы найти красивое решение. В конечном итоге я нашел варианты решения - 1) поменять порядок объектных файлов в мейкфайле, это поменяет порядок инициализации статик членов на нужный (фиговый компайлеро-зависимый вариант); 2) отказаться от регистрации класса в конструкторе, перенеся это в getInstance который проверяет флаг зарегистрированности экземпляра, установленный в false в конструкторе - кривовато, но не зависимо от компайлера. 3) не делать статик экземпляры, а делать статик указатели на них и создавать на куче - не нравится, потому что не понятно что делать в конструкторе, если память не выделилась - код возвращать некуда, а кидание эксепшенов в этом проекте не одобряется. С фабриками делать не стал, мне кажется это еще сильнее усложнит эту часть программы, которую изначально надеялся решить вообще одним классом.
мне нужно все-таки 2 службы для двух таргетов одного проектаМне кажется, что ты с терминологией перегнул. Я вот не понимаю что такое "два таргета одного проекта". И что такое "служба"?
Класс я начал делать чтобы не писать один код 2 раза
Не бойся, пиши 10 раз, это не страшно. На одиннадцатый придёт какое-то решение, которое будет на голову выше остальных. Красивый код рождается не в муках поиска, а сам собой во время практики.
Ну и вообще, ты так и не описал задачу. У тебя классы, фабрики, службы... что хоть делаешь? Подыми голову, посмотри вокруг.
делать статик указатели на них и создавать на куче - не нравится, потому что не понятно что делать в конструкторе, если память не выделилась - код возвращать некуда, а кидание эксепшенов в этом проекте не одобряется.госпадикакойбред...
Служба - это service win32, та для которой можно делать net start и net stop.
госпадикакойбредконтрнебред в студию. а то теоретически критиковать каждый горазд.
не понятно что делать в конструкторе, если память не выделилась - код возвращать некуда, а кидание эксепшенов в этом проекте не одобряетсяохеть! Действительно непонятно! А знаете, почему? Потому что исключения как раз и созданы, чтобы их кидать из конструктора! Не хотите исключений — не используйте
ладно, тут надо помочь с проблемой, а все начали обсирать Простите
не кидать исключения - не мое решение.
теперь хочется красивое решение, но это уже опционально и не к спеху.
хотя нет, я уже предложил подход: надо решить, зачем синглтоны тут нужны? Для глобальной точки доступа или для ограничения количества? Теоретически вот нафига ограничивать количество? Так ли это обязательно? Попробуй от этого отказаться (в надежде, что никто и не будет пытаться создавать второй экземпляр а потом и от глобальности. И нормально В main сконфигурируй эти свои классы, а дальше уж как-нибудь их пропихни куда надо... Не все ж синглтонами писать...
а, т.е. уже можно обсирать?
ага, только конструктивно, а не в стиле "ООП используют одни пидарасы"
не кидать исключения - не мое решение.т.е. если в конструкторе проблема — флаг какой-то выставляется, или что?
нет, по возможности избегается код в конструкторе, который может вызвать проблемы.
Ну да, делайте тогда методы Init...
Давай разбираться. Это означает, что память в конструкторе на стеке выделяется?
примерно так и делается. но имхо это не красиво.
да, на стеке. или в сегменте данных.
Оставить комментарий
elenangel
возникла такая задачка. есть класс A и шаблон B, объявленный как потомок AbstractB. оба синглтоны. при инстанциировании шаблона в конструкторе он добавляет свой this в список класса A. объявлено это дело таким образом:так вот, изначально в классе A тоже его единственный экземпляр был объявлен не как указатель, а как сам класс, но получалось что к моменту инстанциирования шаблона он еще не создан, а конструктор B уже пытается в его список указатель на себя добавить.
можно ли как-то заставить с++ вначале создавать синглтон A, и только после него создавать инстансы B<>?