Проект NULL

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

    Ниже представлен «проект NULL», тот самый костяк, с которого обычно все и начинается. У меня.

    Данный пост скорее всего не будет интересен тем кто уже матерый и тем кто на прямую не связан с разработкой на С++, т.к. ниже представленные материл несет одну единственную цель — дать готовый фундамент для начала.

    Требования


    Попробую для начала выделить ряд требований которые было бы неплохо реализовать в этом костяке
    1. Проектная папка
    2. скрипты сборки CMake и скрипты для генерации солюшинов
    3. скрипты запуска и останова
    4. приложение должно стартовать, работать и корректно завершаться, весь этот процесс должен логироваться
    5. логировать можно и в консоль, все равно в каждом проекте свой логгер, главное, что бы его было легко заменить.
    6. приложение должно разбирать командную строку
    7. приложение должно уметь разбирать конфигурационный файл вида key = value
    8. проект без boost? не, не слышал. Так что сразу интегрируем boost
    9. обработка ошибок. Так как это только костяк и тут, по сути, никакого перформенса нет, то делаем на исключениях.
    10. делаем функцию захвата мира

    Проектная папка


    .
    ├── CMakeLists.txt
    ├── gen_eclipse.sh
    ├── include
    │   ├── logger.h
    │   ├── mediator.h
    │   ├── pid.h
    │   ├── program_options.h
    │   ├── thread.h
    │   └── version.h
    ├── package.sh
    ├── src
    │   ├── logger.cpp
    │   ├── main.cpp
    │   ├── mediator.cpp
    │   ├── pid.cpp
    │   ├── program_options.cpp
    │   └── version.cpp
    ├── start.sh
    ├── stop.sh
    └── version.sh
    

    Генератор солюшинов


    Цель скрипта gen_eclipse.sh это подготовить структуру папок и вызвать cmake для генерации debug и release солюшинов. А так же задать текущую версию проекта. Так сложилось, что разработка на Linux системах у меня обычно ведется в среде Eclipse, отсюда и название gen_eclipse. Но полноценно сдружить Cmake и Eclipse у меня так и не получилось. Для того, что бы сгенерированный проект открыть в Eclipse нужно сделать импорт уже существующего MAKE проекта, при том или release или debug, и через контекстное меню добавить ссылки на директории include и src.
    gen_eclipse.sh
    #!/bin/bash
    
    ROOT_DIR=$PWD
    BUILD_DIR=$PWD/"build"
    BUILD_DIR_R=$BUILD_DIR/release
    BUILD_DIR_D=$BUILD_DIR/debug
    
    mkdir -p $BUILD_DIR
    mkdir -p $BUILD_DIR_R
    mkdir -p $BUILD_DIR_D
    
    if [ -d $BUILD_DIR_R ]; then
    	if [ -f $BUILD_DIR_R/CMakeCache.txt ]; then
    		rm $BUILD_DIR_R/CMakeCache.txt
    	fi
    fi	
    
    if [ -d $BUILD_DIR_D ]; then
    	if [ -f $BUILD_DIR_D/CMakeCache.txt ]; then
    		rm $BUILD_DIR_D/CMakeCache.txt
    	fi
    fi	
    
    echo "[[ Generate Release solution]]"
    cd $BUILD_DIR_R
    cmake -G "Eclipse CDT4 - Unix Makefiles" -DCMAKE_BUILD_TYPE:STRING="Release" --build $BUILD_DIR_R ../../
    
    echo
    echo "[[ Generate Debug solution]]"
    cd $BUILD_DIR_D
    cmake -G "Eclipse CDT4 - Unix Makefiles" -DCMAKE_BUILD_TYPE:STRING="Debug" --build $BUILD_DIR_D ../../
    
    cd $ROOT_DIR
    ./version.sh
    


    Версия


    Первое что стоит отметить, это то, что я использую Subversion и в качестве версий полагаюсь на номера ревизий. Я обычно придерживаюсь следующего формата версии: MAJOR.MINOR.REVISION. Первые два значение задаются ручками, третий это ревизия svn. Насколько мне известно, клиент subversion не умеет возвращать просто номер ревизии, поэтому я использую следующий механизм
    REVISION=`LANG=C svn info | grep "Last Changed Rev:" | sed s/"Last Changed Rev":\ //`
    
    if [[ "$REVISION" == "" ]]; then
    	echo "Cannot recognize number of revision"
    	exit 1
    fi
    ...
    VER_CPP=src/version.cpp
    echo "#include \"version.h\"" > $VER_CPP
    echo "const char* VERSION = \"$VERSION\";" >> $VER_CPP
    

    Скрипты запуска, останова


    Как правило, весь софт, который приходилось писать под Linux, это были сервера, большие и маленькие. Особенность их в том, что они работают в фоне, это службы. Я знаю, что для таких вещей принято иметь в директории init.d скрипты запуска и останова. Но! У меня еще не было ни одного случая, когда на одном сервере запускали бы только одну версию службы. Поэтому я придерживаюсь практики start stop скриптов с контролем по PID файлу.
    start.sh
    #!/bin/bash
    
    source init.conf
    MAIN_LOG="$APP_LOG_DIR"/start.log
    
    echo "Start application '$APP_NAME'"
    
    if [ -f $APP_PID ]; then
            PID=`cat $APP_PID`
           
            if [ -z $PID ]; then
                    echo "File '$APP_PID' exist but it's empty, delete it"
                    rm $APP_PID
            elif ! ps h -p $PID > /dev/null; then
                    echo "File '$APP_PID' exist but process with pid '$PID' doesn't exist, delete it"
                    rm $APP_PID
            else
                    echo "$APP_NAME already started (file $APP_PID exist)"
                    exit
            fi
    fi
    
    mkdir -p $APP_LOG_DIR
    
    if [ $APP_EXPORT_LIB_DIR ]; then export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$APP_EXPORT_LIB_DIR; fi
    
    echo =========================================== >> $MAIN_LOG
    date >> $MAIN_LOG
    
    if [ -f $APP_BIN ]; then
            ./$APP_BIN -l $APP_LOG_DIR -c $APP_CONF -p $APP_PID >> $MAIN_LOG &
    else
            echo "Error: binary file '$APP_BIN' doesn't exist"
            exit 1
    fi
    
    if [[ $? != 0 ]]; then
            echo "Not started"
    else
            echo "Started"
    fi
    


    Скрипт останова имеет куда более изощренную логику, для медленного останова сервера.
    stop.sh
    #!/bin/bash
    
    source init.conf
    
    if [ ! -f $APP_PID ]; then
            echo "'$APP_NAME' not started (file $APP_PID doesn't exist)"
            exit
    fi
    
    PID=`cat $APP_PID`
    
    if ! ps h -p $PID > /dev/null
    then
            echo "'$APP_NAME' not started, removing old $APP_PID file"
            rm $APP_PID
            exit
    fi
    
    if ! kill -s SIGTERM $PID
    then
            echo "Cannot stop process"
            exit
    fi
    
    for i in {1..10}
    do
            if ps h -p $PID > /dev/null
            then
                    echo -n .
                    sleep 1
            else
                    echo "Stopped"
                    exit
            fi
    done
    
    echo
    echo "Can't correctly stop application, finish him"
    kill -9 $PID
    rm $APP_PID
    


    PS. Спасибо моему коллеге Андрею за то, что предложил более юзабельную версию скрипта stop.sh

    Package.sh


    В каждом из проектов у меня есть скрипт package.sh цель которого, создать достаточный инсталляционный пакет. Как правило это заархивированная папка приложения с набором файлов достаточным для работы приложения. Минимальный набор это скприты запуска останова, конфигурационный файл, само приложение, и папка для логов.
    package.sh
    #!/bin/bash
    
    APP_NAME=projectnull
    
    VERSION=`./version.sh show`
    PACKAGE=$APP_NAME.$VERSION.tar.bz2
    
    echo "Create instalation package of '$APP_NAME' ($PACKAGE)"
    
    TEMP_FOLDER=$APP_NAME
    FILES=( "build/release/projectnull"
    "start.sh"
    "stop.sh"
    "init.conf"
    "*.conf" )
    
    LOG_DIR=logs
    
    if [ -d $TEMP_FOLDER ]; then
            rm -rf $TEMP_FOLDER
    fi
    
    mkdir $TEMP_FOLDER
    
    for i in "${FILES[@]}"
    do
            echo "copy '$i'"
            cp $i $TEMP_FOLDER
    done
    
    echo creat $LOG_DIR
    mkdir $TEMP_FOLDER/$LOG_DIR
    
    tar -cjf $PACKAGE $TEMP_FOLDER
    rm -rf $TEMP_FOLDER
    
    echo Finished
    


    Функционал


    И так, что же мне обычно нужно, что бы приступить непосредственно к программированию:
    1. Параметры командной строки первой важности
    2. Файл конфигурации
    3. Простой способ взаимодействия с логгером
    4. Возможность для корректной остановки приложения

    Начнем по порядку:

    Параметры командной строки первой важности


    Я выделил для себя три таких параметра. Сейчас я попробую объяснить, почему именно они.
    Директория для логирования. Причина, по которой я не храню этот параметр в конфигурационном файле, это то, что в процессе разбора конфигурационного файла уже могут случиться ошибки, которые я хочу логировать. Почему директория? Я привык, что каждый запуск это отдельный лог файл, таким образом, легче удалять старые логи.
    Конфигурационный файл Если не через командную строку, то как? Особенно если у вас несколько конфигураций, которые вы хотите быстро переключать.
    PID файл. Единственная причина, почему я не храню его в конфигурационном файле это то, что данный параметр используется сразу в 2 местах. В start и stop скриптах. И гораздо проще вынести его в отдельный файл init, который подключается к start stop скриптам, и править его один раз, чем два (я про conf файл).

    Разбор командной строки и файла конфигурации, производится средствами boost::program_options
    program_options.cpp
    void ProgramOptions::load(int argc, char* argv[])
    {
    	options_description desc("Allowed options");
    	desc.add_options()
    	    ("help,h", "produce help message")
    	    ("config,c", value<std::string>(&conf_file)->default_value(std::string(CONF_FILE)), "set configuration file")
    	    ("logdir,l", value<std::string>(&log_dir)->default_value(std::string(LOG_DIR)), "set log directory")
    		("pidfile,p", value<std::string>(&pid_file)->default_value(std::string(PID_FILE)), "set pid file")
    	;
    
    	variables_map vm;
    	store(parse_command_line(argc, argv, desc), vm);
    	notify(vm);
    
    	if (vm.count("help")) {
    	    std::cout << desc << "\n";
    	    exit(0);
    	}
    	std::cout << "Will be used the next options:" << std::endl
    				<< "CONF_FILE = " << conf_file << std::endl
    				<< "LOG_DIR = " << log_dir << std::endl
    				<< "PID_DIR = " << pid_file << std::endl
    			;
    }
    


    Каждый из параметров имеет значения по умолчанию
    ./projectnull -h
    Allowed options:
    -h [ --help ] produce help message
    -c [ --config ] arg (=project.conf) set configuration file
    -l [ --logdir ] arg (=logs) set log directory
    -p [ --pidfile ] arg (=project.pid) set pid file

    Логирование


    Я не стал изобретать свой логгер, как правило, в каждой фирме он свой. В данном проекте я ограничился выводом в консоль, в режимах Note и Error. Единственно требование, которое я предъявляю к логгеру это то, что он должен поддерживать интерфейс подобный printf. Согласитесь со мной ведь printf это прекрасно. Я лишь добавил макросы для удобного процесса логирования.
    logger.h
    #define ENTRY __PRETTY_FUNCTION__
    #define LOG_0(s)	;
    #define LOG_1(s) Log::note(ENTRY, s)
    #define LOG_2(s, p1) Log::note(ENTRY, s, p1)
    #define LOG_3(s, p1, p2) Log::note(ENTRY, s, p1, p2)
    #define LOG_4(s, p1, p2, p3) Log::note(ENTRY, s, p1, p2, p3)
    #define LOG_5(s, p1, p2, p3, p4) Log::note(ENTRY, s, p1, p2, p3, p4)
    
    #define LOG_X(x,s,p1,p2,p3,p4,FUNC, ...)  FUNC
    #define LOG(...) LOG_X(,##__VA_ARGS__,\
    					LOG_5(__VA_ARGS__),\
    					LOG_4(__VA_ARGS__),\
    					LOG_3(__VA_ARGS__),\
    					LOG_2(__VA_ARGS__),\
    					LOG_1(__VA_ARGS__),\
    					LOG_0(__VA_ARGS__)\
    				)
    
    

    LOG("Appication started, version: %s (%s)", VERSION, BUILD_TYPE);
    

    Output:
    [N][int main(int, char**)]Appication started, version: 1.0.3 (RELEASE)


    Останов


    На мой взгляд, корректная остановка, это одна из важнейших функций ПО о которой часто забывают. Как правило, сделать корректную остановку в уже разработанном ПО это непосильная задача. Другое дело, если придерживаться определенной стратегии с самого начала, то это становится пустяком. Я не рассматриваю всякие изощренные способы получения команды останова по сети, по SMS или через спутник. Я просто ловлю некоторые сигналы, после чего инициирую процедуру корректного останова.
    void Mediator::wait_exit()
    {
    	LOG("Set up waiting exit");
    
    	sigset_t set;
    	int sig;
    
    	sigemptyset(&set);
    	sigaddset(&set, SIGINT);
    	sigaddset(&set, SIGQUIT);
    	sigaddset(&set, SIGTERM);
    	sigaddset(&set, SIGTSTP);
    	sigprocmask(SIG_BLOCK, &set, NULL);
    
    	sigwait(&set, &sig);
    	switch (sig) {
    		case SIGINT:
    		case SIGQUIT:
    		case SIGTERM:
    		case SIGTSTP:
    			LOG("Catched signal to stopping application");
    			stop();
    		break;
    	}
    }
    

    Единственное что требуется это вызвать функцию wait_exit() в главном потоке, после выполнения всех активный действий.
    	LOG("Appication started, version: %s (%s)", VERSION, BUILD_TYPE);
    	{
    		Mediator mediator;
    		mediator.start();
    		mediator.wait_exit();
    	}
    	LOG("Applicatiom stopped");
    

    Спасибо mejedi за указание некорректности использования обработчика сигналов для данных нужд. Надеюсь я правильно истолковал(реализовал) ваше предложение.

    Структура приложения


    Вот мы и добрались к финальной части. Как уже многим стало ясно, я использую паттерн «Медиатор». Конечно, не во всей его красе, ибо никакой бизнес логики пока нет.
    class Mediator: public Thread
    {
    public:
    	Mediator();
    	virtual ~Mediator();
    
    	void wait_exit();
    
    private:
    	virtual void run();
    
    	void load_app_configuration();
    	void create_pid();
    
    private:
    	Pid pid_;
    };
    

    Если какую то работу предполагается выполнять в отдельном потоке, то для такой задачи должен существовать отдельный класс, унаследованный от специального класса Thread.
    class Thread
    {
    public:
    	void start() {th_ = boost::thread(boost::bind(&Thread::run, this));}
    	void stop() {th_.interrupt(); th_.join();}
    
    	virtual ~Thread(){}
    private:
    	virtual void run() = 0;
    private:
    	boost::thread th_;
    };
    

    Цель которого поддержать в едином формате процесс запуска и останова.

    Репозиторий


    Проект доступен в Google Code, но только для read-only. Это не потому что я жадный, я просто не знаю как открыть доступ для всех. При желании вносить правки, пишите ваш g-email, добавлю вас к проекту.
    svn checkout http://project-null.googlecode.com/svn/trunk/ project-null-read-only
    

    Пара слов...


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

    Спасибо за внимание.

    PS. Добавлены различные проверки PID файла при запуске. Обрабатываются кейсы файл с PID есть — процесса нет, PID файл пустой.
    Share post

    Comments 77

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

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

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

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

            Например, от тестов.
              0
              Кстати на счет тестов это вы хорошо вспомнили, вопрос важный, так как если это уже было бы включено в общую структуру тогда начать писать тесты становиться еще проще. Беда только в том что, что кто то любит Gtest, ктото boost test, ктото CppTest. Но наверно я добавлю в проект boost test.
            0
            Т.е., у вас жизнь проекта начинается с запускающего проекта для тестов? ;)
              0
              Для тестов не нужен запускающий проект, для этого есть test harness.
                0
                ну а что именно вы используете в этом качестве?.. Я недавно открыл для себя eclipse с автозапуском PyUnit по сохранению файлов — это какая-то песня :)
                  +1
                  Visual Studio + Resharper.
            0
            А меня в кайф начинать проект с нуля. Практически моментально в голове подбираются нужные фреймворки и архитектура и можно начинать писать.
            Пишу я правда на Джаве.
              0
              на яве то проще с нуля, имхо. Как и на питоне например. А на сиплюсах надо и мейк файлы делать и т.д.
                0
                Так ведь Qt Creator вроде тоже позволяет «сесть и начать»? Причем там совссем необязательно, как я понимаю, использовать Qt-библиотеки — можно любые другие подключать…
              0
              Плюс вам за старание, но, кажется у всех должны уже быть свои скелеты для старта.
                0
                На 50% я рассматриваю эту страничку как «для себя на будущее». В том числе, возможно, что бы в будущем посмеяться над собой :)
                0
                эдакий c++ bootstrap?
                  –11
                  main.cpp — плохой стиль в большом проекте.
                    +4
                    А где по вашему должна находиться точка входа?
                      –9
                      в файле с осмысленным названием.
                        +6
                        Пример в студию. По мне так 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();
                        }
                        
                          –8
                          Хотя бы по названию самого проекта.
                            +12
                            Зачем? В чем сакральный смысл? Это точка входа, она должна называться main.
                              +4
                              Возможно, какие-то плюсы в этом и есть.
                              Но имея файл main.cpp в структуре файлов гораздо легче «раскрутить» логику и архитектуру приложения в целом. Особенно, если вы имеете дело с чужим кодом, который требуется изучить как можно быстрее. И далеко не всегда диаграмма классов, которую можно сгенерировать из исходного кода, даст исчерпывающее понимание архитектуры.
                            +5
                            И ищи потом эту точку входа среди сотен файлов. Вот из-за таких мастеров саппорт превращается в сущий ад.
                        +1
                        Для простого бутстрапа на cmake'е юзаю набор костылей.
                        github.com/gorthauer/cmake-utils

                          +2
                          Простите, но я так и не понял, в чём суть вашего проекта.
                            0
                            Зафиксировать личный опыт. Узнать что в можно улучшить изменить, возможно помочь кому то, кто еще менее опытен чем я. Понятное дело, что ничего нового я не сказал. Я и не пытался.
                            0
                            Согласен с подходом, сам давно уже написал себе пакет генераторов для 'скелета' проекта и для внутреннего исполнения в зависимости от таблиц уже записанных в базу данных ( таблица бд -> DAO, VO, Delegate, Action, Form, JSP, config, build ). При этом можно генерировать разные необходимые детали, компоненты или вертикальные структуры нужные для специфического use case. Очень помогает для быстрого начала любой пользовательской функции (ну и потом дорабатывается руками).

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

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

                                  Таки у меня ошибка, конечно же version.sh должен использоваться Last Change Revision.
                                    0
                                    Конечно, имхо. Но все же не ояень хорошая идея хранить много проектов в одном репозитории. Один проект — один репозиторий. Впрочем, как будет угодно.
                                  +1
                                  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:\ //`
                                  
                                    0
                                    Спасибо, меня мучила эта проблема, но я не знал что делать.
                                    0
                                    Вот бы такую же штуку, только без привязки к программированию вообще. Этакий «скелет любого проекта» — было бы супер!
                                      +1
                                      Простите, конечно, но почему у Вас репозитАрий?))
                                        0
                                        Спасибо, исправил.
                                        –1
                                        1) Неправильная иерархия папок для организации заголовочных файлов. Твоя реализация предполагает включение файлов таким образом:
                                        #include "program_options.h"
                                        #include "version.h"
                                        А должно подразумевать указание «пространства имён» (как в Бусте и везде):
                                        #include "MyCompany/MyProject/program_options.h"
                                        #include "MyCompany/MyProject/version.h"
                                        2) Такая иерархия файлов плохо масштабируется. Попробуй рассмотреть пример не отдельно взятого проекта в вакууме, а взаимодействие пары пользовательских модулей (например, library и runner), заданных заголовками и исходниками, и пары сторонних библиотек, заданных заголовками и предкомпилированными бинарниками.
                                          0
                                          А должно подразумевать указание «пространства имён» (как в Бусте и везде):
                                          #include «MyCompany/MyProject/program_options.h»
                                          #include «MyCompany/MyProject/version.h»

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

                                          Не совсем улавливаю вашу мысль. Если вы хотите подключить к этому проекту стороннюю библиотеку, то у вас есть возможность просто положить ее рядом в inclide/other_lib или же сделать свой файндер для CMake и хранить ее далеко в системе. Не?
                                            0
                                            На счет С++ 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 сторонним пользователям.
                                              0
                                              А в чем проблема конкретно с «version.h», его же никто напрямую не включает. Из MySuperLib.h, лежащего рядом (в тойже директории) его можно включать как «version.h», поскольку «путь» разрешается относительно текущего файла а потом уже относительно include path.

                                              При этом сам MySupeLib.h можно включать как #include<foo/bar/MySuperLib.h>, это ничему не противоречит.
                                                0
                                                А в чем проблема конкретно с «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 можно спокойно перемещать, потому что
                                                // он использует не относительный путь, а отсчитываемый от корня.
                                                  0
                                                  Ок, значит чисто технически никаких проблем нет, и вы это признаете. А проблемы, которые появятся при разрастании этой конкретной библиотеки можно решать по мере их поступления, поскольку публичный интерфейс от этого не меняется.

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

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

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

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

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

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

                                                    Их лучше не решать, а сразу избегать. И никогда не использовать относительные пути в скриптах сборки, директивах включения, 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. Остальные библиотеки тащатся в дерево проекта. Да, в основном в собранном в виде. В исключительных случаях — в виде исходников, и включаются в скрипты сборки. Но, опять же, к обсуждаемой теме это отношения не имеет.
                                                      0
                                                      Так я об этом и говорю. Только в include path должна включаться не папка libName/, а её родитель. Чтобы использование было не #include «header.h», а #include «libName/header.h». Но это не только на пользовательской стороне; так же требуется, чтобы этого соглашения придерживался автор библиотеки.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                                            Этот проект как раз и нацелен быть корневым. Но опять таки, это не ключ от всех дверей, и все проблемы решить он не может.
                                            +2
                                            Извините, это полнейшее порно. Вот так у вас выглядит обработчик сигналов:
                                            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).
                                              0
                                              На счет обработчика сигналов я осведомлен. У вас есть решение лучше?

                                              За конструктивные замечание спасибо, попробую прокомментировать:
                                              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 это нужно. Но это уже планы на будущее.
                                                0
                                                Это не демон. По сути это просто консольное приложение которое в случае необходимости можно отправить в background. Главное что бы логирование было в файл а не в консоль. Возможность сделать его демоном, интересная, но пока не было нужды.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                                            Only users with full accounts can post comments. Log in, please.