Pull to refresh

Comments 77

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

TDD спасет отца русской демократии.

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

Я тоже. Но мне это не очень нравится. Конечно всех коней в вакууме не предусмотришь в начале, как бы ни старался, но проще отталкиваться уже от чего то, чем от пустого main.cpp
проще отталкиваться уже от чего то

Например, от тестов.
Кстати на счет тестов это вы хорошо вспомнили, вопрос важный, так как если это уже было бы включено в общую структуру тогда начать писать тесты становиться еще проще. Беда только в том что, что кто то любит Gtest, ктото boost test, ктото CppTest. Но наверно я добавлю в проект boost test.
Т.е., у вас жизнь проекта начинается с запускающего проекта для тестов? ;)
Для тестов не нужен запускающий проект, для этого есть test harness.
ну а что именно вы используете в этом качестве?.. Я недавно открыл для себя eclipse с автозапуском PyUnit по сохранению файлов — это какая-то песня :)
А меня в кайф начинать проект с нуля. Практически моментально в голове подбираются нужные фреймворки и архитектура и можно начинать писать.
Пишу я правда на Джаве.
на яве то проще с нуля, имхо. Как и на питоне например. А на сиплюсах надо и мейк файлы делать и т.д.
Так ведь Qt Creator вроде тоже позволяет «сесть и начать»? Причем там совссем необязательно, как я понимаю, использовать Qt-библиотеки — можно любые другие подключать…
Плюс вам за старание, но, кажется у всех должны уже быть свои скелеты для старта.
На 50% я рассматриваю эту страничку как «для себя на будущее». В том числе, возможно, что бы в будущем посмеяться над собой :)
main.cpp — плохой стиль в большом проекте.
А где по вашему должна находиться точка входа?
в файле с осмысленным названием.
Пример в студию. По мне так main.cpp нужен и должен выглядеть вот так:

#include <QApplication>
#include <qqml.h>
#include "declarativeview.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    a.setApplicationName("vreen");
    a.setOrganizationName("vreen");
    a.setOrganizationDomain("https://github.com/gorthauer/vreen");

    DeclarativeView view;
    return a.exec();
}
Хотя бы по названию самого проекта.
Зачем? В чем сакральный смысл? Это точка входа, она должна называться main.
Возможно, какие-то плюсы в этом и есть.
Но имея файл main.cpp в структуре файлов гораздо легче «раскрутить» логику и архитектуру приложения в целом. Особенно, если вы имеете дело с чужим кодом, который требуется изучить как можно быстрее. И далеко не всегда диаграмма классов, которую можно сгенерировать из исходного кода, даст исчерпывающее понимание архитектуры.
И ищи потом эту точку входа среди сотен файлов. Вот из-за таких мастеров саппорт превращается в сущий ад.
Простите, но я так и не понял, в чём суть вашего проекта.
Зафиксировать личный опыт. Узнать что в можно улучшить изменить, возможно помочь кому то, кто еще менее опытен чем я. Понятное дело, что ничего нового я не сказал. Я и не пытался.
Согласен с подходом, сам давно уже написал себе пакет генераторов для 'скелета' проекта и для внутреннего исполнения в зависимости от таблиц уже записанных в базу данных ( таблица бд -> DAO, VO, Delegate, Action, Form, JSP, config, build ). При этом можно генерировать разные необходимые детали, компоненты или вертикальные структуры нужные для специфического use case. Очень помогает для быстрого начала любой пользовательской функции (ну и потом дорабатывается руками).

До сих пор еще думаю может стоит поставить как открытый код, но как и с другими проектами, которые так поставил, потом придется поддерживать, а вот этим как раз и не хочется заниматься в данном случае.
Прежде чем начинать новый проект, стоит рассмотреть существующие реализации. Вдруг кто-то эту задачу уже решил до вас
> Насколько мне известно, клиент subversion не умеет возвращать просто номер ревизии, поэтому я использую следующий механизм

svnversion
svnversion это номер последней ревизии всей ветки. Я использую ревизию последнего изменения проектной папки. В случае когда у вас в svn только один проект разницы нет. Но если более, то это уже не то.

Таки у меня ошибка, конечно же version.sh должен использоваться Last Change Revision.
Конечно, имхо. Но все же не ояень хорошая идея хранить много проектов в одном репозитории. Один проект — один репозиторий. Впрочем, как будет угодно.
REVISION=`svn info | grep Revision | sed s/Revision:\ //`

if [[ "$REVISION" == "" ]]; then
    REVISION=`svn info | grep Редакция: | sed s/Редакция:\ //`
fi

А если в команде появится немец с локалью de_DE? Чтобы корректно распарсить вывод команды, нужно просто выставлять LANG=C:

REVISION=`LANG=C svn info | grep Revision | sed s/Revision:\ //`
Спасибо, меня мучила эта проблема, но я не знал что делать.
Вот бы такую же штуку, только без привязки к программированию вообще. Этакий «скелет любого проекта» — было бы супер!
Простите, конечно, но почему у Вас репозитАрий?))
1) Неправильная иерархия папок для организации заголовочных файлов. Твоя реализация предполагает включение файлов таким образом:
#include "program_options.h"
#include "version.h"
А должно подразумевать указание «пространства имён» (как в Бусте и везде):
#include "MyCompany/MyProject/program_options.h"
#include "MyCompany/MyProject/version.h"
2) Такая иерархия файлов плохо масштабируется. Попробуй рассмотреть пример не отдельно взятого проекта в вакууме, а взаимодействие пары пользовательских модулей (например, library и runner), заданных заголовками и исходниками, и пары сторонних библиотек, заданных заголовками и предкомпилированными бинарниками.
А должно подразумевать указание «пространства имён» (как в Бусте и везде):
#include «MyCompany/MyProject/program_options.h»
#include «MyCompany/MyProject/version.h»

Я ни в коем случае не буду использовать название компании в исходных кодах. Вы наверно пишите на Java или C#, потому что вот именно там я такую штуку видел очень часто. Я даже имя проекта в сорсах не использую.
На счет С++ namespace, я их специально не использую тут, потому что не знаю какие.
2) Такая иерархия файлов плохо масштабируется. Попробуй рассмотреть пример не отдельно взятого проекта в вакууме, а взаимодействие пары пользовательских модулей (например, library и runner), заданных заголовками и исходниками, и пары сторонних библиотек, заданных заголовками и предкомпилированными бинарниками.

Не совсем улавливаю вашу мысль. Если вы хотите подключить к этому проекту стороннюю библиотеку, то у вас есть возможность просто положить ее рядом в inclide/other_lib или же сделать свой файндер для CMake и хранить ее далеко в системе. Не?
На счет С++ namespace, я их специально не использую тут, потому что не знаю какие.

Насчёт C++ namespace я ни слова не упомянул. Я говорил про квалификацию имён файлов префиксами директорий.

Я ни в коем случае не буду использовать название компании в исходных кодах.

Если не название компании, то хотя бы название библиотеки, если гарантирована уникальность её имени. (Название компании просто позволяет избежать проблем, если кто-то другой использует библиотеку с таким же именем, а кто-то третий использует обе библиотеки, скажем, PngUtils, SvgTools, etc.)

Вы наверно пишите на Java или C#, потому что вот именно там я такую штуку видел очень часто.

В том числе и на C#. И эта «частая штука» там неспроста.

Не знаю ни одной широкоизвестной библиотеки, где заголовки включались бы как
#include "version.h"
Везде используется паттерн
#include "SomeLibrary/version.h"
#include "AnotherLibrary/version.h"
...
(А нет, одну знаю, но она написана китайцами.)

Я даже имя проекта в сорсах не использую.

Ну вот у тебя есть заголовок version.h и в своих заголовках ты его подключаешь как version.h, и пользователю твоей библиотеки предполагается подключать его как version.h. А что будет, если у пользователя есть свой файл version.h, а ещё он использует какую-то китайскую поделку, где есть свой version.h? При разворачивании препроцессором директив включения, как он должен понять, какой из version.h где включать?

Я даже имя проекта в сорсах не использую.

Тут мы имеем твоё мнение против мнения авторов Boost, Box2D, SFML, Loki, etc. Все они внутри (и для внешних пользователей это предполагается тоже) включают заголовки отсчитывая от корня с именем проекта.

Мне кажется, что ты свою схему просто не обкатывал на проектах, состоящих из множества модулей, использующих несколько сторонних API и в свою очередь предлагающих себя как API сторонним пользователям.
А в чем проблема конкретно с «version.h», его же никто напрямую не включает. Из MySuperLib.h, лежащего рядом (в тойже директории) его можно включать как «version.h», поскольку «путь» разрешается относительно текущего файла а потом уже относительно include path.

При этом сам MySupeLib.h можно включать как #include<foo/bar/MySuperLib.h>, это ничему не противоречит.
А в чем проблема конкретно с «version.h», его же никто напрямую не включает.

Конкретно version.h, может, никто и не включает. А какой-то другой файл, скажем, кто-то будет включать; utils.h, например. Имя не важно, дальше я буду продолжать использовать version.h для примера.

При этом сам MySupeLib.h можно включать как #include<foo/bar/MySuperLib.h>, это ничему не противоречит.

И что это за «foo/bar/»? Мне эту директорию самому надо создать, и копировать в него заголовки из папки MySuperLib/include/?

поскольку «путь» разрешается относительно текущего файла а потом уже относительно include path.

Другой вопрос. Появится субмодуль Module/, а в нём файл foo.cpp. Как он должен включать version.h?
// Module/foo.cpp

#include "version.h"
// 1) Нет разрешения относительно текущего файла, будет поиск в include path,
// где равноправны «мой» и «его» экземпляры version.h.

#include "../version.h"
// 2) Теперь файл Module/foo.cpp тяжело перемещать по необходимости (например, вглубь),
// потому что помимо его перемещения в Module/Subsubmodule/ нужно _исправить его код_,
// добавив больше «точек»: #include "../../version.h"

#include "MyLibrary/version.h"
// 3) Текущий файл Module/foo.cpp можно спокойно перемещать, потому что
// он использует не относительный путь, а отсчитываемый от корня.
Ок, значит чисто технически никаких проблем нет, и вы это признаете. А проблемы, которые появятся при разрастании этой конкретной библиотеки можно решать по мере их поступления, поскольку публичный интерфейс от этого не меняется.

Между прочим все более-менее серьезные библиотеки собираются и устанавливаются и только потом их используют. Я имею в виду, что имеется какая-то система сборки, которая производит бинарники библиотеки. Причем эта система сборки совершенно не связана с той системой, которая собирает приложение, например нет «утечки» include-path. Далее бинарники и публичные заголовки либо устанавливаются в регламентированные локации на файловой системе, либо вручную тащатся в папку проекта.

Внутренние заголовки, тот же version.h например, используются только при сборке либы и наружу не отдаются. Подводя итоги — для пользователя либы есть готовые бинарники и публичные заголовки, все остальное его волновать не должно.

Как бы хорошо ссылаться на буст, но надо понимать, что это нетипичный пример. Из-за того, что подавляющее большинство входящих в его состав либ — header-only, им приходится намного сильнее заморачиваться с организацией заголовочных файлов.

Подавляющему же числу библиотек достаточно примитивной плоской структуры вида libName/libName.h, где дополнительные публичные заголовочные файлы живут в той же папке libName/.
Ок, значит чисто технически никаких проблем нет, и вы это признаете.

Нет, не признаю, я описал эти проблемы. (Можно на «ты», тем более знакомы в жеже же.)

А проблемы, которые появятся при разрастании этой конкретной библиотеки можно решать по мере их поступления

Их лучше не решать, а сразу избегать. И никогда не использовать относительные пути в скриптах сборки, директивах включения, etc.

Внутренние заголовки, тот же version.h например, используются только при сборке либы и наружу не отдаются.

Я специально оговорил это в предыдущем комментарии. Не нравится приватный заголовок version.h, подставляй вместо него в мои комментарии публичный заголовок utils.h. Имя не важно, в примерах я просто считаю version.h публичным заголовком (API), используемым как автором библиотеки, так и пользователем.

Причем эта система сборки совершенно не связана с той системой, которая собирает приложение, например нет «утечки» include-path.

«Утечка» есть. Ещё раз рассмотри пример выше с Module/foo.cpp; все приведённые там рассуждения относятся также и к публичному заголовку Module/foo.h. Как из него подключить «version.h»? Как «version.h», как "../version.h" или как «MyLibrary/version.h»? Я показал, почему первые два способа плохи.

Подавляющему же числу библиотек достаточно примитивной плоской структуры вида libName/libName.h, где дополнительные публичные заголовочные файлы живут в той же папке libName/.


Так я об этом и говорю. Только в include path должна включаться не папка libName/, а её родитель. Чтобы использование было не #include "header.h", а #include "libName/header.h". Но это не только на пользовательской стороне; так же требуется, чтобы этого соглашения придерживался автор библиотеки.

Как бы хорошо ссылаться на буст, но надо понимать, что это нетипичный пример.

Да почти все широко используемые библиотеки используют это соглашение. Я привёл пример четырёх навскидку, но могу перечислить гораздо больше (тупо по списку пройтись).

Между прочим все более-менее серьезные библиотеки собираются и устанавливаются и только потом их используют.

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

Далее бинарники и публичные заголовки либо устанавливаются в регламентированные локации на файловой системе, либо вручную тащатся в папку проекта.

У нас в команде «установка в систему» (не регламентированную локацию, а в ту, куда удобнее разработчикам) происходит только для Boost. Остальные библиотеки тащатся в дерево проекта. Да, в основном в собранном в виде. В исключительных случаях — в виде исходников, и включаются в скрипты сборки. Но, опять же, к обсуждаемой теме это отношения не имеет.
Так я об этом и говорю. Только в include path должна включаться не папка libName/, а её родитель. Чтобы использование было не #include «header.h», а #include «libName/header.h». Но это не только на пользовательской стороне; так же требуется, чтобы этого соглашения придерживался автор библиотеки.

Полностью поддерживаю.

Я просто немного о другом сказать пытаюсь. ИМХО практика писать чистый, поддерживаемый и расширяемый код, весь из себя ООП по канонам банды четырех — сильно переоценивается. То есть это конечно все хорошо и правильно, только имеет смысл исключительно для больших проектов, причем большинство разработчиков склонны занижать пороговую величину на порядок(ки).

Также и здесь — в проектах уровня Boost/Qt надо очень вдумчиво подходит к файловому лэйауту. Но здесь явно не тот случай. И кстати идиотский лейаут это не так страшно. Реально видел как люди писали перловые скрипты, чтобы переколбасить структуру заголовочных файлов под себя (Heimdal который интегрирован в макось, сотни мегабайт кода).
ИМХО практика писать чистый, поддерживаемый и расширяемый код, весь из себя ООП по канонам банды четырех — сильно переоценивается… только имеет смысл исключительно для больших проектов...

Это ж не о том немного. Если было бы два способа, один из которых правильный и муторный, а второй — рабоче-крестьянский и быстрый, то да, можно обсуждать, какой где применим.

А в случае с файловой иерархией это два равных по начальным трудозатратам подхода. Только один экономнее по последующим трудозатратам, и уже обкатан практикой. И это применимо не только к C++, но и к Си тоже.

И кстати идиотский лейаут это не так страшно.

Ох, не скажи. Мне приходится работать с вышеупомянутой китайской библиотекой. У них два десятка папок, в некоторых по несколько уровней подпапок. И включение в своих заголовках (в том числе глубоко вложенных) производят так, будто они просто есть в include path. Соответственно, в скриптах сборки нужно указать гадзиллион каталогов, да ещё этот набор немножко разный для трёх целевых платформ.

А я хочу один раз в скрипте сборки добавить в include path папку вроде $(Qbit_ExtInclude)chineselib-1.2.3/include/ (там лежит единственная папка chineselib), а везде в коде писать явно #include "chineselib/magicheader.h" вместо просто #include "magicheader.h"
Посоветую ка я вам обоим книгу «John Lakos — Large-Scale C++ Software Design» — в ней большая часть книги отведена под физический дизайн: как располагать файлы, в какой структуре и т.п.
С вашим уровнем внимания к деталям будет интересно.
Не знаю ни одной широкоизвестной библиотеки

Начну с того что я не предоставляю библиотеку. Это даже близко на нее не похоже и цели такой тоже не было. На этом пожалуй и закончу :)

Я согласен со всем что вы написали про библиотеки. Но сравнивать это с тем что делал я это так же, как говорить данный проект идет ортогонально C# идеологии — абсолютно верно, но бессмысленно.
Я согласен со всем что вы написали про библиотеки. Но сравнивать это с тем что делал я это так же, как говорить данный проект идет ортогонально C# идеологии — абсолютно верно, но бессмысленно.

Возможно, ты прав, я неоправданно примерил шаблон на себя. Просто любой код, что я пишу на C++ — это всегда разрабатывается как библиотека (даже если нет пользователей кроме её разработчиков, и API — один класс). Если мне нужен в конечном счёте исполняемый файл, то проект будет в виде библиотека + тонкий runner с точкой входа, не содержащий логики кроме вызова методов библиотеки. В дальнейшем удобно накидывать разные раннеры для разных платформ, или раннеры тестов, демок, etc.

Начну с того что я не предоставляю библиотеку. Это даже близко на нее не похоже и цели такой тоже не было. На этом пожалуй и закончу :)

Если речь не о промышленной разработке, а о накидывании прилаг (демонов) «для себя» или однократных экспериментов в стол, то да, наверное, не стоит заморачиваться с точки зрения использования «проекта NULL» как библиотеки. Но зачем тогда туда тащить версию из, прости господи, Subversion, пять шелл-скриптов и генерацию скриптов сборки CMake'ом?
то проект будет в виде библиотека + тонкий runner с точкой входа,

Который должен уметь почти все что выше описанный проект :)

Если речь не о промышленной разработке,

Любая разработка начинается с чего то. Это один из вариантов с чего я начинаю.
Который должен уметь почти все что выше описанный проект :)

Да, ок. Правда в этом случае папка include/ избыточна. Она обычно содержит публичные заголовки, которых в демоне нет. В библиотеках приватные (чисто технические, не торчащие наружу) заголовки по возможности лучше прятать в src/ — они выступают только в роли исходников, но не интерфейса.

Любая разработка начинается с чего то. Это один из вариантов с чего я начинаю.

А как этот шаблон продолжать наполнять «мясом», то есть библиотеками, содержащими фактический код, а не инфраструктурную обвязку?
Правда в этом случае папка include/ избыточна.

В библиотеках приватные (чисто технические, не торчащие наружу) заголовки по возможности лучше прятать в src/

На счет этого у меня еще не сформировано мнение. Но я не люблю *.h и *.cpp файлы держать в перемешку. Но вот идея про то, что в include должны быть публичные хедеры заставляет задуматься.

А как этот шаблон продолжать наполнять «мясом», то есть библиотеками, содержащими фактический код, а не инфраструктурную обвязку?

Вам рассказывать как программировать? Я не встречал библиотеки которыми можно пользоваться на уровне
main(){
     OtherLib.ResolveMyProblem();
}
Но вот идея про то, что в include должны быть публичные хедеры заставляет задуматься.

Более того, иногда случается даже так, что пользователю должны поставляться и некоторые приватные хедеры. Т.е. хедеры, содержащие детали реализации, которые: 1) должны быть доступны из интерфейсных заголовков, 2) не предполагается, что пользователь будет их явно включать. Обычно (Boost, Loki) такие заголовки кладутся в подпапки detail или details.

Но я не люблю *.h и *.cpp файлы держать в перемешку.

Косметически, может, и некрасиво. Но о них стоит думать не как об «h'никах и cpp'шках», а как про «код и интерфейс». В Бусте это нормальная практика.

Вам рассказывать как программировать?

Я имею в виду, как согласовывать их файловые структуры, зависимости по сборке (топологическая сортировка, таймстемпы), etc. Или предполагается, что общего корня сборки у них не будет, они будут изолированы в никак не связанных между собой каталогах? Работа ведь ведётся одновременно и над раннером, и над библиотекой, и над другой библиотекой, от которой зависит первая, etc.

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

Этот проект как раз и нацелен быть корневым. Но опять таки, это не ключ от всех дверей, и все проблемы решить он не может.
Извините, это полнейшее порно. Вот так у вас выглядит обработчик сигналов:
src/mediator.cpp
void exit_sig_handler(int sig)
{
        Mediator::exit();
}

void Mediator::exit()
{
        LOG("set exit");
        {
                boost::lock_guard<boost::mutex> lock(exit_mtx_);
                exit_ = true;
        }
        exit_cv_.notify_one();
}


Так делать нельзя никогда. Есть очень ограниченное число функций, которые можно использовать в контексте обработчика сигналов. Проблема в том, что сигнал прерывает выполнение кода в случайный момент времени — в этот момент могут быть захвачены блокировки и некоторые структуры данных могут находиться в неконсистентном состоянии.

Например макрос LOG(), который у вас реализован через printf, для начала захватывает блокировку в потоке вывода. Если в момент прихода сигнала программа что-то выводила, эта блокировка окажется уже захваченной, а в результате дедлок.

1. Лицензия, GPL3. Для многих будет неприемлимо.
2. Сигналы — демоны должны обрабатывать SIGTERM (остановка) и SIGHUP (обновление конфигурации).
3. Использование ревизии из svn для версионирования — плохое решение. Как минимум тем, что привязывает к svn.
4. «Сборку дистрибутива» — ваш package.sh — умеет делать CMake.
5. Задание имени pid-файла через опцию командной строки я считаю излишним. у всех всегда пути жестко вкомпилированы, то же самое относится и к логам. При необходимости пути должны настраиваться во время конфигурирования, перед сборкой.
6. Если это демон, то почему не проиходит демонизации (создание новой сессии, отказ от управляющего терминала и тд.)? Имеет смысл сделать поддержку upstart (Linux), launchd (MacOS) и scm (Windows).
На счет обработчика сигналов я осведомлен. У вас есть решение лучше?

За конструктивные замечание спасибо, попробую прокомментировать:
1. Лицензия, GPL3. Для многих будет неприемлимо.

я даже не задумывался о лицензии, но думаю поменять для красоты не помашет, какую лучше поставить?
2. Сигналы — демоны должны обрабатывать SIGTERM (остановка) и SIGHUP (обновление конфигурации).

Это не демон. По сути это просто консольное приложение которое в случае необходимости можно отправить в background. Главное что бы логирование было в файл а не в консоль. Возможность сделать его демоном, интересная, но пока не было нужды.
На счет обновления конфигурации. Это задача сложная, не достаточно просто обновить значения параметров, нужна какая то логика перехода от старых к новым значениям. Это уже частные случай. Я могу лишь добавить другой обработчик для обновление конфигурации. НО! В отличии от выключения, в этом случае я уже не решусь в обработчике делать сложные вычисление, без коих обновление конфигурации навряд ли сработает.
3. Использование ревизии из svn для версионирования — плохое решение. Как минимум тем, что привязывает к svn.
Для, для GIT это подойдет. И я не знаю как сделать автоверсионирование для GIT. Без него не хочу.
4. «Сборку дистрибутива» — ваш package.sh — умеет делать CMake.

Он проде позволяет сделать правила для make install. Не? Это немножко не то.
5. Задание имени pid-файла через опцию командной строки я считаю излишним. у всех всегда пути жестко вкомпилированы, то же самое относится и к логам. При необходимости пути должны настраиваться во время конфигурирования, перед сборкой.
Я с вами не согласен. И я в статье явно написал, что одно из требований это возможность запуска нескольких инстансов на одном сервере.
6. Если это демон, то почему не проиходит демонизации (создание новой сессии, отказ от управляющего терминала и тд.)? Имеет смысл сделать поддержку upstart (Linux), launchd (MacOS) и scm (Windows).

Это не демон :) Но вот добавить скрипты для запуска останова, хотя бы еще для Win это нужно. Но это уже планы на будущее.
Это не демон. По сути это просто консольное приложение которое в случае необходимости можно отправить в background. Главное что бы логирование было в файл а не в консоль. Возможность сделать его демоном, интересная, но пока не было нужды.

Значит я вас неправильно понял — а понял я что предлагается шаблонный проект для быстрого старта при создании демона под UNIX.

Как бы вы сформулировали цели своего проекта, решаемые задачи и для кого он?

Лицензия — Apache, BSD или MIT.

Пофиксить обработчик сигналов, с сохранением текущей семантики, можно при помощи pipe. (В обработчике писать кусок данных в пайп, ждать завершения при помощи чтения. Можно заменить на signalfd в новых линуксах. Или тупо sigsuspend в выделенном потоке.)

По поводу автоматического версионирования — подумайте, а нужно ли вам оно? Обычно номер версии поддерживается руками и меняется вручную перед релизом. Есть смысл зашивать в бинарник номер билда или информацию о ревизии в системе контроля версий (тупо в виде строки свободного формата), если централизованной системы сборки нет.
Как бы вы сформулировали цели своего проекта, решаемые задачи и для кого он?
Быстро начать писать прототип. Очень часто прототипы перерастают в рабочие проекты. В первую очередь для меня. Во вторую, для тех кто найдет его структуру полезной для себя. Это не ключ от всех дверей. Это не Boost.
Лицензия — Apache, BSD или MIT.

А сделать «Other Source License», а далее просто не упоминать о ней, нормально? В смысле так сойдет? Просто я плаваю в лицензиях, мне проще вообще не рассматривать этот кусок кода как лицензируемый.
Пофиксить обработчик сигналов, с сохранением текущей семантики, можно при помощи pipe. (В обработчике писать кусок данных в пайп, ждать завершения при помощи чтения. Можно заменить на signalfd в новых линуксах. Или тупо sigsuspend в выделенном потоке.)

В черновом варианте поста была строка: «Я не рассматриваю сложные механизмы останова, такие как посыл команды по сети, SMS или со спутника». Pipe это еще сложнее чем сеть. Работать с ними легко, работать правильно и отказоустойчивость сложно. К примеру если создать Pipe, повиснуть в блокирующем чтении, а потом снаружи удалить файл Pipe — ничего не случится. Вообще ничего. Поэтому я стараюсь не использовать пайпы.
Быстро начать писать прототип. Очень часто прототипы перерастают в рабочие проекты. В первую очередь для меня. Во вторую, для тех кто найдет его структуру полезной для себя.

Прототипы надо писать на PythonУ меня есть некоторые сомнения, что настолько специфичная вещь (приложение которое ведет себя как демон, но демоном не является) будет пользоваться спросом.

Pipe это еще сложнее чем сеть. Работать с ними легко, работать правильно и отказоустойчивость сложно. К примеру если создать Pipe, повиснуть в блокирующем чтении, а потом снаружи удалить файл Pipe — ничего не случится.

Это файлы fifo, а я предлагал pipe. Это стандартное решение, кстати говоря.
Это же не именованные пайпы. Простите не понимаю. Может у вас есть пример. Потому что если это не будет громоздко и решит проблему с сигналами, вот это как раз то что я с радостью добавлю, как минимум для себя.
Сейчас постараюсь расписать имеющиеся варианты.

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

2. Синхронная обработка сигнала. Заводим отдельный тред, в котором делаем sigsuspend/sigwait (ждать прихода сигнала). Так как о приходе сигнала мы узнаем в «нормальном контексте», можно безопасно пользоваться любыми функциями.

3. Получение нотификации через файловый дескриптор. Крайне удобно, когда обрабатывается много сокетов в одном потоке (select или более современные аналоги). Заводим анонимный пайп, оба конца которого находятся в нашем процессе. Из обработчика сигнала пишем кусок данных в пайп, в результате другой конец становится годным для чтения, о чем нам сообщает select.

Отмечу что на винде система запускает обработчики сигналов в отдельном потоке, поэтому там все проще.
Быстро начать писать прототип. Очень часто прототипы перерастают в рабочие проекты

Не надо так делать.
Извините, это полнейшее порно. Вот так у вас выглядит обработчик сигналов:
src/mediator.cpp

Я бы назвал это скрытым порно :) Кто то даже не поймет какой ужас тут происходит. Я мирюсь с этим, просто потому, что сделать лучше не могу.
Не хватает опроса в конце статьи, добавляю сразу результат.

1 буду использовать (1%)
2 использую другое решение (7%)
3 использую свое решение (80%)
4 всегда пишу с нуля (12%)
«Я очень долго вычитывал эту стать и прогонял ее через Word, но я все равно уверен, что в ней остались ошибки»
И здесь тоже осталась… :)
Проект доступен в Google Code, но только для read-only. Это не потому что я жадный, я просто не знаю как открыть доступ для всех.

Если открыть доступ на запись для всех, то кто угодно сможет вносить любые правки, в том числе неосознанно ломать или зловредно вандалить.

При желании вносить правки, пишите ваш g-email, добавлю вас к проекту.

Не стоит добавлять к проекту незнакомых людей. Механизм правок от посторонних лиц в системах управления версиями называется pull request (не знаю поддерживается ли в Google Code).
Кстати, почему для проекта в век DVCS выбрана централизованная система контроля версий?
Я бы на github залил, там люди смогут легко делать форки и отправлять правки-предложения.
Cupper
3 из 3-х где я работал используют SVN. И я не думаю что доживу до века когда git будет повсеместно использоваться в коммерческих фирмах.

Когда я выкладывал проект, я задумался о git, мол «дайка попробую», потом увидел что там нет понятие ревизия, на которой я привык основывать версию. Погуглил, как люди делают автоверсионирование. Ничего толкового не нашел. Понял, что пока не готов.
Смотря какие продукты
В вебе часто используется гит (у нас например, в небольшой компании), ну и в крупных тоже (в Google активно используют гит).
UFO just landed and posted this here
А я просто не использую QT.
Кстати, про ревизию из SVN — возможно ещё одно улучшение (использовал в проекте, оказалось удобно).
Можно добавить в конце ещё одну цифру-флажок, которая будет указывать на наличие изменений.
Например, если из svn r123 получается build 1230 — это чистый билд из 123-й ревизии. Можно в продакшн! (если нужно).
А если из неё же получился build 1235 — то это билд из 123-й ревизии с незакоммитанными изменениями. Он, возможно, подойдёт тестеру, но в случае релиза нужно зафиксировать коммит и из него пересобрать уже чистый 1240.
Использование ревизии SVN в качестве номера билда это сомнительная практика.
Это политика фирмы и личные предпочтения.
Простите но это плохая практика делать билд с локальными изменениями. У нас в один прекрасный момент случился такой билд, и так случилось, что вот именно его признали стабильным и отдали в публичное тестирование. Теперь мы каждый раз вздрагиваем когда нам нужно отдать тот билд. Потому что ни кто не знает, какие локальные изменения там были.
Практика сама по себе не плохая. Например, в процессе отладки или тестирования. Вмешивать в типичный прогон «поправил — собрал — прогнал тест — обнаружил ошибку» ещё и коммит — совсем тоскливо. Незачем мусорить в репу выражением самого процесса.
Вот когда всё зафиксили — там да, коммит обязателен. У нас стоял хук-скрипт, который запрещал выкладывать на паблик билды с нечётными номерами. Однако при попытке так сделать не происходило ничего страшного. Всего лишь автоматически запускался коммит и открывалось окошко для ввода описания ревизии.
Sign up to leave a comment.

Articles