среда, 30 ноября 2011 г.

Мультиплатформенная среда разработки

Вот уже 10 лет пользуюсь такой замечательной средой для разработки, как FTE.
Это легкий, в прямом смысле, редактор исходного кода. Элегантно совмещен с панелью выбора файлов для редактирования, запуском всевозможных утилит. Есть подсветка синтаксиса.
Главное достоинство - мультиплатформенность. Не нужно каждый раз при переходе с одной платформы на другую (например с windows на unix) привыкать к среде разработки.

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

вторник, 29 ноября 2011 г.

Разработка и компиляция на удаленных серверах

Ситуация, когда платформы находятся на другой стороне земного шара - хорошо знакома многим из нас. И хотя сейчас, в век оптоволокна, все не так и плохо, но все же локальная разработка и компиляция все же предпочтительнее.
Итак, какие инструменты на сегодняшний день доступны?
  1. Хорошо зарекомендовавший себя, терминальный доступ по протоколу SSH (например через putty).
  2. Удаленный доступ по различным десктоп-ориентированным протоколам, таким как RDP, X, и.т.п.
  3. Различные Web приложения для удаленной разработки.
  4. Приложения - редактирую локально - компилирую удаленно.
От себя могу рекомендовать приложение четвертого типа:
BVRDE - интегрированная среда разработки (IDE) для C и C + +.  
Как пишет автор, "разработан разработчиком для разработчиков". 
BVRDE умеет управлять проектами, есть интегрированный интерактивный отладчик, мощный современный редактор исходного кода. В общем, то, что доктор прописал.






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

понедельник, 28 ноября 2011 г.

C++ библиотека-плагин для Unix. Часть 5-я. Cannot dlopen или как исключить LD_PRELOAD

На HP-UX иногда возникает ситуация, когда стороннее приложение не сможет загрузить нашу библиотеку динамически (dlopen/shl_load). Ошибка возникает когда библиотека содержит внутри статические Thread Local Storage (TLS) либо ссылается на другие библиотеки ее содержащие.
Ошибка выводится на консоль в виде строки:
/usr/lib/pa20_64/dld.sl: Cannot dlopen load module 
'/usr/lib/pa20_64/libcl.2' because it contains thread specific data.

В интернете можно встретить на эту тему советы использовать LD_PRELOAD, например тут.
Это неплохой совет, если у вас небольшое приложение, использующее стандартные менеджеры памяти например.
LD_PRELOAD опасен тем, что он переопределяет естественный порядок нахождения символов в библиотеках и при более сложных зависимостях библиотек - приложения - ведет к ошибкам во времени выполнения.
Как-же быть? Наверно нужно исключить из нашей библиотеки статические секции TLS.
Как я уже говорил выше, статические секции TLS могут содержатся в нашей библиотеке или в сторонних. В большинстве случаев, нужно и можно найти замену сторонней библиотеке, либо, в случае если доступен исходный код, то можно легко заменить статическую секцию на динамическую. Статические секции в коде декларируются определением __thread.
Есть еще один источник статической секции TLS - это библиотека времени выполнения g++: libsupc++.a - при статической линковке или libstdc++.so - при динамической. Динамическую линковку (libstdc++.so) мы можем использовать, если только приложение и наша библиотека собраны одной версией компилятора. Т.е. это не наш случай.
В libsupc++.a статическая секция TLS содержится в объектном файле eh_globals.o:
nm -C eh_globals.o | grep tbss
[12] |0| 0|SECT |LOCAL|0|.tbss._ZZN12_GLOBAL__N_110get_globalEvE6global|
[9]  |0| 8|TLS  |LOCAL|0|.tbss._ZZN12_GLOBAL__N_110get_globalEvE6global|
{_GLOBAL__N_1::get_global()} global


 Для ее устранения, нужно либо компилятор пересобрать, чтобы не использовал статические секции (почитать про опции --with-tls --with-__thread), либо - более простой способ - пересобрать данный файл: eh_globals.c. Для этого, потребуется всего лишь немного его подправить, на предмет зависимостей. Затем из локальной копии удаляем из libsupc++.a оригинальный eh_globals.o и добавляем туда наш.
ar d libsupc++.a eh_globals.o
ar r  libsupc++.a eh_globals_no_tls.o
Используем при линковке локальную копию библиотеки.
Все. Теперь нашей библиотеке не нужен никакой LD_PRELOAD.

пятница, 25 ноября 2011 г.

C++ библиотека-плагин для Unix. Часть 4-я. Сетевые засады: POSIX vs BSD

Есть такая замечательная OS, называется HP-UX. Имеет она целых 2 стека TCP: - POSIX и BSD. И разработчики этой замечательной системы такие юмористы - сделали одинаковые имена функций, но разные типы аргументов (а еще усугубили тем, что аргументы указатели).
Допустим, я разработал библиотеку с учетом использования BSD реализации, а приложение (не мое), которое данную библиотеку использует, было разработано с учетом использования POSIX реализации сокетов. Это приведет к тому, что при любой сетевой активности моей библиотеки (допустим при вызове из нее функции приложением) - мы получаем GPF.
Конечно, мы можем договорится с разработчиком приложения использовать одну и ту же реализацию сокетов. Это если приложение одно такое - использует мою библиотеку. А если их несколько? Как быть с уже не поддерживаемыми приложениями?

Выход мне подсказал клиент oracle - libclntsh.so. Ведь правда, линкуй хоть с POSIX хоть с BSD - все работает и не падает. Стал рыть дальше. Посмотрел какие вызовы sockets использует libclntsh.so. Например: setsockopt

elfdump -t libclntsh.so.10.1 | grep setsockopt
3253   FUNC GLOB 0     UNDEF      0x0000000000000000 0      _setsockopt


Ага, использует вызовы из libc (с подчерком перед символом). Как он это делает, интересно?
Ищем дальше. Смотрим в архивах *.o файлы - ничего там не находим. И вот оно - то что надо (для краткости, я опустил часть вывода):
nm xnetstubs.o
[28]     |                     0|       0|FUNC |GLOB |0|   UNDEF|_accept
[29]     |                     0|       0|FUNC |GLOB |0|   UNDEF|_bind
[30]     |                     0|       0|FUNC |GLOB |0|   UNDEF|_connect
[31]     |                     0|       0|FUNC |GLOB |0|   UNDEF|_getpeername
...

[39]     |                     0|      80|FUNC |GLOB |0|   .text|accept
[40]     |                     0|      80|FUNC |GLOB |0|   .text|bind
[41]     |                     0|      80|FUNC |GLOB |0|   .text|connect
[42]     |                     0|      80|FUNC |GLOB |0|   .text|getpeername
...
Смотрим, как он это дело линкует. Находим в clntsh.map на первой же строчке:
-h accept -h bind -h connect -h getpeername -h getsockname -h getsockopt -h setsockopt -h recv -h recvfrom -h send -h sendto -h setsockopt /u01/app/oracle/product/10.2.0/db/lib/xnetstubs.o
В документации по линкеру - -h это значит скрыть символ. Теперь при линковке  нашей библиотеки добавляем:

-Wl,-h,accept -Wl,-h,bind -Wl,-h,connect -Wl,-h,getpeername 
-Wl,-h,getsockname -Wl,-h,getsockopt 
-Wl,-h,setsockopt -Wl,-h,recv -Wl,-h,recvfrom 
-Wl,-h,send -Wl,-h,sendto -Wl,-h,setsockopt 
${ORACLE_HOME}/lib/xnetstubs.o
Все. Теперь наша библиотека будет использовать BSD сокеты при любых способах линковки основного приложения.

Кстати, таким образом можно и другие вызовы перенаправлять в требуемые библиотеки. (но об этом в другой раз)

четверг, 24 ноября 2011 г.

C++ библиотека-плагин для Unix. Часть 3-я. Наводим порядок с Makefile.

Ни для кого не секрет, что make - самая распространенная система сборки и особенно для unix.
Но make требует особого ухода за Makefile либо каких-то иных, сторонних утилит для его обслуживания. В GNU мире широкое распространение получил autoconf и наверное по праву. У него много достоинств для охвата широкого спектра конфигураций на разных инсталляциях.
В моем же случае autoconf слишком "жирен" и для сборки на моих одних и тех - же машинах повторяет все шаги, тестирует и.т.д.

Для себя же, уже давно выбрал Makefile Project Creator (MPC). Я использую MPC из библиотеки ADAPTIVE Communication Environment (ACE) тем более, что я и саму библиотеку часто использую и в поставке имеются дополнения и улучшения. Также MPC умеет создавать файлы проектов для Visual Studio и других распространенных сред.
Итак, что нам нужно для сборки на разных платформах?
  1. Библиотека ACE. Даже если вам данная библиотека не нужна, MPC в ее поставке отлажен и требует наименьших усилий по доработке. 
  2. Распаковываем ее, ставим переменную окружения ACE_ROOT, указывающую на каталог  ACE_wrappers.
  3. На каждой платформе в каталоге ACE_wrappers/makeinclude/include создаем файл platform_macros.GNU . Включаем туда одну строчку, например для linux:
    include $(ACE_ROOT)/include/makeinclude/platform_linux.GNU
  4. Создаем для нашего проекта mpc файл:
    project (ourproject)  {
      sharedname   = ourlibname
      Source_Files {
         main.cpp
         util.cpp
      }
    }
  5. Получаем наши проектные Makefile - запускаем в каталоге проекта:
    perl ${ACE_ROOT}/bin/mwc.pl   -type gnuace
  6. Запускаем make для сборки.
  7. Добавляем в наш mpc файл другие опции (смотрим в документации в составе ACE).
 Кстати, с помощью MPC очень легко переносить приложения с одной платформы на другую, но об этом в другой раз.

среда, 23 ноября 2011 г.

Использование SVN в связке с ClearCase

Так уж исторически сложилось, что ClearCase наше все. Что уж тут поделаешь?! Это интегрированная система с большими возможностями для анализа и других дел, непонятных простому разработчику. И про тормознутость этого ClearCase рассказывать не приходится. Да еще известная фича - когда ClearCase сервер на виндах то и работает с виндовыми клиентами, и наоборот с unix. Ну да где наша не пропадала?!

Создаем отдельную инфраструктуру на SVN - для этого, погуглив, я нашел то, что доктор прописал: VisualSVN. Это виндовый сервер SVN, есть бесплатная лицензия для коммерческого ипользования, HTTPS опять-же.

Клиенты для VisualStudio есть, но я остановился на TortoiseSVN, как более удобном в эксплуатации. Для своих unix машинок - скомпилял в хоме svn из оригинальных исходников.

Теперь об самом интересном: требуется из SVN периодически переливать изменения в ClearCase. Пришлось перебрать несколько способов, в поиске находились такие, которые или не работали или требовали какие то запредельные права для ClearCase типа CREATE VIEW. Наконец нашел один замечательный проект, написанный на java: svn2cc.
Использую его так:
  1. С помощью svndump создаю изначально полный дамп базы SVN (тут нужен локальный доступ к базе, но подойдет и read-only шара).
  2. Сохраняю последнюю ревизию базы svn в файле.
  3. С помощью svn2cc переливаю все в ClearCase.
  4. Далее использую сохраненную ревизию, чтобы полного дампа не делать, а только инкрементальный.
  5. Опять повторяем шаг 3 и так по кругу, как только надо залить изменения.
Правда есть некоторые ограничения этого способа: например не работает переименование каталога в svn - наверно нужно доработать svn2cc - пока просто: если нужно переименовать - то удаляю и создаю заново в svn.

И на последок: если не нужно в ClearCase переливать историю всех изменений - то достаточно
залить туда вывод svn export с помощью cleartool import и уже потом инкрементально лить изменения.

вторник, 22 ноября 2011 г.

C++ библиотека-плагин для Unix. Часть 2-я. OpenVMS.

Openvms назвать unix-ом скорее всего нельзя. Ну хотя, там за последние 20 лет появились какие то зачатки posix-образности, даже есть GNV (эта так у них GNU называется). Командный процессор (DCL) там тоже не подарок. Хотя, дело привычки.
Итак, о чем это я?!
Как то раз, подкинули мне задачку портировать одну замечательную библиотечку с unix на openvms. Библиотечка c++-ная, многопоточная, внешняя библиотека oracle, да еще к тому же сама
по OCI с соседним oracle работает. Про назначение ее  распространяться не буду.
Один разработчик эту библиотеку месяца три потихоньку на этот предмет до меня ковырял, и сказал мне, что жутко все там тормозит, почему-то. Хоть у нас и не ахти какая быстрая openvms машинка была (alfa какая то, уже не помню), но наши приложения с oracle там довольно таки шустро работали. К нам, в гости, даже по этому поводу приезжал из представительства HP сотрудник отдела openvms. Настроил нам как следует машинку. Библиотечка чуть по шустрее стала работать, но все равно как то не на много.
Как оказалось, все довольно просто. Раньше, в бытность VAX в VMS не слыхивали о нитях (появились только в последних релизах). По этой причине, видимо для совместимости, по умолчанию никаких нитей нет. Чтобы были нити - нужно при линковке указать линкеру специальный квалификатор /THREADS_ENABLE.
Все опции и квалификаторы - смотрим здесь.
Как уточнил @RRL, и я сам тоже начал вспоминать,

для компилера:

/reentrancy=multithread

для линкера:
/threads=(multiple_kernel_threads,upcalls)
И сразу же этой библиотеке полегчало. Правда об еще одном камне на дороге стоит упомянуть:
В openvms размер стека у нити не вмещает OCI-ную нить (которая там штатно, средствами OCI создается), так что пришлось реализовывать вручную для OCI мультинитевость на posix threads.



понедельник, 21 ноября 2011 г.

C++ библиотека-плагин для Unix. Часть 1-я. Visibility

Современные версии g++ (а я решил взять последнюю 4.6.2) обзавелись замечательным атрибутом visibility. Атрибут позволяет как и в windows dll скрывать символы (раньше, все символы в разделяемых библиотеках торчали наружу).
Подробнее об этом замечательном атрибуте можно почитать в первоисточнике GCC Wiki Visibility.

Беда в том, что внося новое, обязательно что нибудь старое сломается. Это как в бородатом анекдоте про программиста, его сына и солнце :) А задача у меня, как я уже писал в предыдущем посте, собрать такую старую добрую большую библиотеку, и чтобы не было ссылок на libgcc_s и libstdc++. Вот эта штука, visibility, мне, как оказалось все портила.

/usr/bin/ld: /usr/local/gcc/lib/i386-redhat-linux/4.6.2/libgcc_eh.a(unwind-dw2.o)(.text+0x2c3): unresolvable R_386_PLT32 relocation against symbol `_Unwind_Find_FDE@@GCC_3.0'
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: выполнение ld завершилось с кодом возврата 1
gmake[1]: *** [libsnmp_int_stlport.so.5.2.1] Ошибка 1
Т.е. как видим из ошибки, линкер не нашел _Unwind_Find_FDE в libgcc_eh.a. Но оно там есть, сам видел:
ar x libgcc_eh.a
nm unwind-dw2-fde-glibc.o | grep Find
   00001af0 T _Unwind_Find_FDE
Грешил я, что gcc при сборке libgcc_eh.a не добавляет -fPIC - но это зря, все он добавляет.
Оказалось, что он просто скрыл данный символ этой новой модной штукой visibility.
Проблему решил, изменив определение
extern const fde * _Unwind_Find_FDE (void *, struct dwarf_eh_bases *);
на
extern __attribute__ ((visibility ("default"))) const fde * _Unwind_Find_FDE (void *, struct dwarf_eh_bases *);
в файле заголовка unwind-dw2-fde.h в исходниках gcc.
Пересобирать весь gcc не надо. Заходим в каталог сборки boot/i686-pc-linux-gnu/libgcc,
удаляем *.o и запускаем gmake libgcc_eh.a. Затем подменяем оригинальную libgcc_eh.a или используем прямое указание компилятору рантаймных библиотек. (об этом позже)
Кстати, в версии 4.3.2. та-же самая проблема .

воскресенье, 20 ноября 2011 г.

Простой способ использования Lua в программе на c++

Пересмотрев большинство библиотек, предназначенных для вызова Lua из C++ и наоборот, пришел к выводу что наиболее удачным и простым будет сочетание LuaClassBasedCall и Lunar.
Из c++ вызов выглядит так (используем  LuaClassBasedCall ):
double result; 
const char* errmsg = L.PCall("local a,b = ...; return a*b",
                     Inputs(3, 2.5), Outputs(result));
Для обратного вызова, используем Lunar. Ну и напоследок, чтобы упростить распаковку и упаковку аргументов обратного вызова из Lua (Lunar), применяем аналогичную технологию шаблонов, как было сделано в LuaClassBasedCall:
template<typename T>
inline void push(lua_State* L, const T& t);

template<typename T>
inline void push(lua_State* L, const T*& t);

template<>
inline void push(lua_State* L, const int& p) {
  lua_pushnumber(L, p);
}

template<>
inline void push(lua_State* L, const unsigned int& p) {
  lua_pushnumber(L, p);
}

template<>
inline void push(lua_State* L, const long& p) {
  lua_pushnumber(L, p);
}

template<>
inline void push(lua_State* L, const unsigned long& p) {
  lua_pushnumber(L, p);
}

template<>
inline void push(lua_State* L, const double& p) {
  lua_pushnumber(L, p);
}

inline void push(lua_State* L, const char *p) {
  lua_pushstring(L, p);
}

template<>
inline void push(lua_State* L, const std::string& p) {
  lua_pushlstring(L, p.c_str(), p.length());
}

template<>
inline void push(lua_State* L, const unsigned long long& p) {
  push(L, lstrutl::to_str(p));
}


template<>
inline void push(lua_State* L, const bool& p) {
  lua_pushboolean(L, p ? 1 : 0);
}
и.т.д.

C++ библиотека-плагин для Unix. Вступление.

Иногда хочется странного: как в старых добрых виндах собрать shared библиотеку, и через dlopen использовать ее в других приложениях. На первый взгляд все хорошо, собираешь библиотеку с помощью g++, линкуешься с опцией -shared, и .... . Но тут вспоминаешь, что эта библиотека С++ , приложения могут быть собраны другой версией g++ и вообще другим компилятором.... Понимаешь, что ты попал. Но как говорится - прорвемся! Об этом в следующий раз.