пятница, 13 апреля 2012 г.

История одного синглтона

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

Моё отношение к паттернам проектирования можно описать так.

Вещь, приносящая пользу исключительно на уровне коммуникаций с коллегами. Разговор "на одном языке" значительно упрощает общение и плодотворно влияет на сроки исполнения проектов. Проще употребить слово "синглтон" (простите, я против употребления в русском языке слова "синглетон"), чем три минуты пояснять особенности создания объектов такого класса и не быть полностью уверенным, что тебя поняли правильно.

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

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

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

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

У каждого программиста, со временем, накапливается собственная коллекция каких-то решений. Кто-то это пытается оформить библиотеками для личного или даже более широкого использования. А у кого-то это лежит просто файлокодом в месте доступном для потребления при создании новых программ.

У меня тоже растет такая коллекция решений. И в текущей коллекции есть все более совершенствующаяся система журналирования, которую я цепляю к разным своим проектам.

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

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

Ход мыслей, который привел к такому решению был, примерно, следующий.

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

2. Одно из решений состоит в проверке некоторого глобального указателя, который должен быть всегда объявлен, но может либо иметь значение ноль (при не созданном объекте журнала), либо указывать на объект журнала.

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

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

4.1. Надо заметить, что, вообще, повторное использование кода тема особенная и даже делая для себя надо делать "для дураков" при всем уважении к себе любимому.

Вот, собственно, и весь синглтон, без претензии на знание соответствующего паттерна.

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


вторник, 10 апреля 2012 г.

Пример заготовки CMakeList.txt для простого проекта


Введение в CMake


Утилита cmake является кроссплатформенной утилитой с открытым исходным кодом (open source), используемой для создания традиционных сценариев сборки проектов для следующих платформ.


  1. Unix/Linux -> Makefile (для утилиты GNU make)
  2. Windows -> VS Projects/Workspaces
  3. Apple -> Xcode

Процесс сборки проекта, при использовании cmake, состоит из следующих этапов.


  1. Создание файла конфигурации CMakeLists.txt в котором через систему команд, свойств и переменных описываются исходные файлы проекта, цель сборки и все сопутствующие сборке детали.
  2. Выполнение утилиты cmake. При это будет произведена обработка файла конфигурации CMakeLists.txt в процессе которой будет сгенерирован файл традиционного для данной операционной системы сценария сборки. Например, Makefile.
  3. Выполнение традиционных сценариев сборки. Например, утилитой make.

Используя специальные генераторы можно привязать систему CMake к определенным системам сборки, таким как Qt, Borland C++ Builder, MS Visual C++ и пр. Это свойство системы, само по себе интересно, но привязка кроссплатформенной сборки к какому-то конкретному продукту, выглядит, в общем случае, неразумно. Не считая, разумеется, вариант с поддержкой Qt SDK. Здесь особый разговор, так как сборка остается кроссплатформенной, но составляет альтернативу собственной системе сборки для Qt, qmake, которая не лишена некоторых недостатков.


Я вспоминаю о CMake всегда, когда не делаю проект в Qt. Вот почему.


  1. Проект не привязывается к какой-то среде разработке и его можно писать хоть в vim. Для небольших проектов это исключительно удобно. Для тех, кто относится к vim с предубеждением путь представят себе вместо vim любой другой редактор плоского кода с подсветкой синтаксиса, который им кажется удобным. Сейчас таких много под любой платформой.
  2. Проект удобно передавать в виде исходников для сборки у заказчика или какого-то иного лица потребляющего этот код. Удобно в контексте его простой компиляции. Не придется ставить среду разработки. Потребуется только наличие CMake, а это уже может быть много проще. Особенно с этим нет проблем в Linux.
  3. Сборка проекта по Makefile, который был сгенерирован системой CMake достаточно зрелищная. Во-первых, выполняется в цвете, а, во-вторых, с отображением процента исполнения.

Из написанного выше не следует, что CMake удобен только для небольших проектов. Просто большие проекты это дело особого обсуждения. Часто тут решение принимает не один человек и, в некоторых случаях, это решение определяется самой постановкой задачи. Зачем, например, городить огород, если задача заказана в MS Visual Studio и будет использована только там. В случае, если CMake допустим для проекта, то это позволит получить удобства описанные в списке выше.


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


CMakeLists.txt для простого проекта


Итак, мы начали простой проект. Создали каталог my-project и создали в нем подкаталог src для размещения источников кода нашего проекта. Напишем заготовку в файл src/main.cpp и теперь настроим CMake для сборки по этому проекту. Потом мы будем добавлять в сборку новые файлы и, возможно, библиотеки. Отразим все эти возможности в нашей заготовке.


Конфигурационный файл для системы CMake должен называться CMakeLists.txt. Расположим его в каталоге my-project и получим следующую систему файлов.


my-project                - каталог проекта
my-project/src            - каталог источников проекта  
my-project/src/main.cpp   - исходный файл (пока один; только *.cpp файла)
my-project/CMakeLists.txt  - конфигурационный файл системы CMake 

Представим шаблон файла CMakeLists.txt для простого проекта.




cmake_minimum_required ( VERSION 2.6 )
# Укажите здесь имя вашего проекта.
# Учитываете установку переменных 
# <project_name>_BINARY_DIR и  <project_name>_SOURCE_DIR
# в значение имени каталога с проектом.
project ( project_name )

set ( SRC_DIR ${PROJECT_SOURCE_DIR}/src )

# Укажите вместо my-project имя своего приложения
# - имя исполняемого файла
set ( APP_NAME my-project )

# Управление типом сборки через задание значения
# предопределенной переменной CMAKE_BUILD_TYPE
# Возможные значения:
# DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL
set( CMAKE_BUILD_TYPE RELEASE )

# Снять комментарий для добавления пути на 
# заголовочные файлы, которые следует добавить 
# для компиляции проекта
#include_directories ( /path/to/headers_1 )
#include_directories ( /path/to/headers_2 )
# ...
#include_directories ( /path/to/headers_N )

# Снять комментарий для добавления пути на 
# библиотеки, которые следует добавить 
# для линковки проекта
#link_directories ( /path/to/lib_1 )
#link_directories ( /path/to/lib_2 )
# ...
#link_directories ( /path/to/lib_N )

list(APPEND SRC ${SRC_DIR}/main.cpp )
# снять комментарий для добавления других *.cpp файлов
# list(APPEND SRC ${SRC_DIR}/file_1.cpp )
# list(APPEND SRC ${SRC_DIR}/file_2.cpp )
# ...
# list(APPEND SRC ${SRC_DIR}/file_N.cpp )

add_executable ( ${APP_NAME} ${SRC} )

# Снять комментарий для добавления имени библиотеки
# требуемой для линковки проекта
#target_link_libraries( ${APP_NAME} name_1 )
#target_link_libraries( ${APP_NAME} name_2 )
# ...
#target_link_libraries( ${APP_NAME} name_N )



Рассмотрим значение представленных в файле команд.


cmake_minimum_required - выставляет требование по минимальной версии CMake, требуемой для обработки даного файла конфигурации.


project(<project_name>) - задает имя проекта. Необходимо учитывать, что дополнительной функцией данной команды является установка переменных <project_name>_BINARY_DIR и <project_name>_SOURCE_DIR в значение имени каталога проекта.


set - задает значение переменной для дальнейшего использования. Здесь создаем переменную SRC_DIR по значению подставленному из предопределенной переменной PROJECT_SOURCE_DIR и подстроки "/src". Чтобы получить значение переменной, её идентификатор надо поместить в фигурные скобки и предварить знаком доллара - ${...}.


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


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


list с первым аргументом APPEND выполняет добавление к списку, указанному вторым аргументом, элемента заданного третьим аргументом. В нашем случае, к списку под именем SRC добавлено имя файла main.cpp с путем, указанным значением переменной SRC_DIR.


add_executable - для сборки исполняемого файла представленного первым аргументом добавляет компиляционные листы, которые будут образованы по списку файлов источников, заданных вторым аргументом - значением списка SRC.


target_link_libraries - задает библиотеки для линковки. Исполняемый файл указывается первым аргументом. Например, если в Linux требуется указать для линковки приложения my-project библиотеки с именем файла libm.so, то следует написать target_link_libraries(my-project m), что соответствует общим правилам задания имени библиотек в опциях для линковки. В одной команде можно указать несколько библиотек, разделив их пробелом.


Представленный вариант файла CMakeList.txt можно взять как шаблон для своих проектов. Чтобы собрать такой проект, надо в каталоге с файлом CMakeLists.txt выполнить команду "cmake ." (не забудьте про точку, обозначающую синоним для имени текущего каталога - в нем будет осуществлён поиск файла CMakeLists.txt). По этой команде будет создан Makefile, который потом следует обработать утилитой make. Для этого надо просто выполнить команду "make".


Полезные ссылки


  1. http://www.cmake.org - официальный сайт системы CMake
  2. http://symmetrica.net/cmake/ - интересные подробности по CMake. В том числе использование для проектов wxWidget и Qt.
  3. При работе в Linux, документацию по установленной системе CMake можно посмотреть в каталоге /usr/share/doc/cmake-data.

Мои статьи по CMake.


  1. Boost и CMake для Windows
  2. Boost и CMake для Windows (подробности)