Инструменты пользователя

Инструменты сайта



// MinGW: кросс-отладка win32 приложений в Linux

По сути парочка дополнений для этой статьи: http://mingw-cross.sourceforge.net/cross_debug.html

Задача: есть win32 приложение, собранное путём кросс-компиляции при помощи cmake и mingw32, нужно запустить его в отладчике и поймать на чём оно падает.

Необходимый инстументарий:

  1. установленный mingw32
  2. wine (winconsole)
  3. бинарная версия gdb для windows, брать тут:
    http://sourceforge.net/projects/mingw/files/MinGW/Extension/gdb/
  4. версия gdb, собранная для Linux, знающая как подгружать win32 приложения, собранные mingw. Тут нужно искать сборку для вашего дистрибутива, для пользователей ArchLinux я сделал PKGBUILD и поместил его в AUR:
    https://aur.archlinux.org/packages.php?ID=54802
    продвинутые пользователи могут подглядывать в мои правила сборки и собирать самостоятельно :)

Бинарную версию нужно будет распаковать, например в ~/bin/mingw32-gdb-win32/. В директорию ~/bin/mingw32-gdb-win32/bin/ при этом следует сделать следующие симлинки:

cd ~/bin/mingw32-gdb-win32/bin/
ln -s /usr/i486-mingw32/bin/libexpat-1.dll .
ln -s /usr/i486-mingw32/bin/libiconv-2.dll .
ln -s /usr/i486-mingw32/bin/libintl-8.dll .

Всё. Впринципе всё готово для отладки.

  1. Запускаем сервер:
    wineconsole cmd /K ~/bin/mingw32-gdb-win32/bin/gdbserver.exe localhost:6000 test-app.exe [аргументы для программы с которыми она должна запускаться]
  2. Запускаем клиент gdb:
    i486-mingw32-gdb test-app.exe
  3. В запущенной версии gdb выполняем следующую команду:
    (gdb) target remote localhost:6000

Ну и по сути всё, дальше делаем всё как при обычной отладке при помощи gdb, устанавливаем точки останова, смотрим данные, делаем бектрейсы и т.д. Единственная разница, что для запуска приложения нужно использовать не команду run, а команду continue, но собственно если забудете или перепутаете - отладчик вам подскажет.

Если программа что-то выбрасывает на консоль, вы увидете это в окне wineconsole.

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

// winsock2: неблокирующийся сокет

    unsigned long arg = 1;
    ioctlsocket(sockfd, FIONBIO, &arg);

в Linux:

    int arg = fcntl(sockfd, F_GETFL, NULL);
    arg |= O_NONBLOCK;
    fcntl(sockfd, F_SETFL, arg);

или универсальная функция:

void setSockNonblock(int sockfd)
{
#ifdef WIN32
    unsigned long arg = 1;
    ioctlsocket(sockfd, FIONBIO, &arg);
#else
    int arg = fcntl(sockfd, F_GETFL, NULL);
    arg |= O_NONBLOCK;
    fcntl(sockfd, F_SETFL, arg);
#endif
}

// inet_aton для windows

Если коротко: функиця заполняет структуру типа in_addr, преобразуя сведения о хосте из строкового представления.

У winsock2 нет такой функции. Есть более продвинутый аналог inet_pton, в POSIX он тоже есть, да вот только mingw про неё в windows не знает. Пичалька.

Поэтому делаем примерно следующее:

#ifdef WIN32
static int inet_aton(const char *cp, struct in_addr *inp)
{
    if (cp == 0 || inp == 0)
    {
        return -1;
    }
 
    unsigned long addr = inet_addr(cp);
    if (addr == INADDR_NONE || addr == INADDR_ANY)
    {
        return -1;
    }
 
    inp->s_addr = addr;
}
#endif

не верх совершенства и корректности, но для моих целей работало.

// Таймеры в Windows

Внезапно, правда? :)

Но волей судеб нужно было портировать программку, в которой использовался nanosleep(2). И…

Для внезапно стало откровением: В WINDOWS НЕТ ТАЙМЕРОВ ВЫСОКОГО РАЗРЕШЕНИЯ. А есть только по сути хаки и вызов Sleep, с минимальным промежутком в 10мс.

Наиболее часто рекомендуемый вариант:

void sleep_nsec(int64_t interval)
{
    int64_t time1 = 0, time2 = 0, sysFreq = 0;
 
    QueryPerformanceCounter((LARGE_INTEGER *) &time1);
    QueryPerformanceFrequency((LARGE_INTEGER *) &sysFreq);
 
    do
    {
        QueryPerformanceCounter((LARGE_INTEGER *) &time2);
    } while(((time2-time1)*1000.0*1000.0/(double)(sysFreq)) < interval);
}

Но тут мы упираем процессор в полку. Есть вариант с использованием select, но, по отзывам, он так же может ждать не менее 10мс.

Т.е., по сути, это единственный вариант для Windows, да ещё и не лишённый глюков: почти все процессоры уже умеют автоматическое понижение частоты… Чуете? Пока работает цикл, значение sysFreq может измениться (поправьте меня, если я не прав) и время потекло.

Кроме того, наткнулся на статью:
http://www.codeproject.com/KB/system/timers_intro.aspx - Timers Tutorial

Описываются различные вариант таймеров: стандартные win32, мультимедийные, Waitable Timers (не знаю как лучше перевести на русский язык), Queue Timers. Но опять таки, все периоды - миллисекунды.

// Альтернативная сборка MinGW

Занимаясь разработкой небольшой программы, использующей сокеты, потоки и не использующей фреймворков, типа Qt, столкнулся с необходимостью собрать это дело для Windows. Подводных камней оказалось много, как результат познакомился с такими милыми сущностями, как Boost.Thread и Boost.Asio.

Но тут вспомнилось, как мне уже приходилось собирать под Windows окружение, для компиляции исходников, использующих Boost и стало грустно. Грустно настолько, что оказалось проще сделать бинарники путём кросс-компиляции. И вот, когда дело было сделано, натыкаюсь на этот ресурс:
http://nuwen.net/mingw.html

Стефан (автор этого поделия) предоставляет сборку MinGW включающую сам компилятор, набор полезных библиотек (включая Boost 1.47.0!) и некоторых полезных утилит. Пакет не требует установки, просто распаковываем в c:\mingw и пользуемся.

Так что на будущее, буду пробовать.

// Qt Creator & doxygen

Сегодня пролетело в рассылке:

Hi,

Qt Creator master has recently got a feature, which has been requested a
few times, for Doxygen blocks generation. If you type /** or /*! and
press enter/return before a declaration, you'll get something like:
/*!
 *
 * \param data
 * \param options
 * \return
 */
QString generate(const Data &data, const Options &options);


You can also opt whether or not you want an additional \brief command
and leading asterisks for comment continuation (when breaking lines).
The options are in Text Editor→Completion→Documentation Comments.

Notice that the style is identified from the comment beginning. If you
start it with /*! then the Qt style is used. Otherwise, the Java style
with the @ prefix for the commands is picked.

Cheers,
Leandro

Проверил - работает, удобно :)

// Hibernate + SQLite

Шаг 1. Взять sqlite-jdbc отсюда: http://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC или при помощи Maven (описание по той же ссылке)

Шаг 2. Положить у себя в проект класс SQLiteDialect, взятый отсюда: http://arnoraps.tweakblogs.net/blog/3146/hibernate-with-sqlite.html или из архива отсюда: http://code.google.com/p/hibernate-sqlite/ (HibernateSQLite.zip)

Шаг 3. В конфигурацию Hibernate добавить примерно следующее:

  <!-- SQLite settings -->
  <property name="hibernate.connection.driver_class">org.sqlite.JDBC</property>
  <property name="hibernate.connection.url">jdbc:sqlite:./data.db</property>
  <property name="hibernate.dialect">ru.sincore.db.SQLiteDialect</property>
 
  ...
 

// Jetty, JSP и Maven

На правах заметки:

  • Список зависимостей для Maven, что бы получить последнюю (8.0.4) версию embedded Jetty и получить рабочий JSP.
  • Конфиг для embedded Jetty, что бы завёлся JSP.

Подробности подкатом

// Hibernate Tutorial

Отличный, по моему мнению, туториал по Hibernate:

особенно хорошо показаны построения отношений между таблицами.

// QLandkarteGT: Экспорт всей карты в растр, а не только видимой области

В QLandkarteGT есть удобная вещь - экспорт видимой области карты со всеми отображаемыми точками, треками, измерениями и т.п. в растровый (PNG) файл.

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

Патч подкатом.

// Поломанная привязка карт в QLandkarteGT

Сегодня столкнулся с тем, что привязывая карту в QLandkarteGT, получаю такую ошибку:

/usr/bin/gdal_translate -a_srs +proj=merc +a=6378245.0000 +b=6356863.0188 +towgs84=24,-123,-94,-0.02,0.25,0.13,1.1,0 +units=m  +no_defs -gcp 108 182 inf inf -gcp 3310 189 inf inf -gcp 3308 3126 inf inf -gcp 93 3119 inf inf -gcp 1704 1657 inf inf /home/hatred/maps/1/src/K-53-26-view-raw.tiff /tmp/qt_temp.B12852
Input file size is 3402, 3428
0...10...20...30...40...50...60...70...80...90...100 - done.

/usr/bin/gdalwarp -order 1 -r cubic -dstnodata "255" /tmp/qt_temp.B12852 /tmp/qt_temp.u12852

ERROR 1: Attempt to create -2147483648x-2147483648 dataset is illegal,sizes must be larger than zero.
Creating output file that is -2147483648P x -2147483648L.

Неудачно!

Сначала было подумал, что поломано в SVN версии, однако, в релизной 1.2.4 ситуация точно такая же.

Сразу и сходу смутили параметры inf в опции -gcp. Так что решил поглядеть в коде, благо собираю из SVN и он у меня всегда под рукой.

Немного времени, расстановка логов, анализ привели к тому что обнаружилось двойное преобразование координат в функции

bool GPS_Math_Str_To_LongLat(const QString& str, float& lon, float& lat, const QString& srcproj, const QString& tarproj)

в файле GeoMath.cpp

Первое преобразование было в блоке кода:

if(GPS_Math_Str_To_Deg(str, lon, lat,true))
 {
   if(pjTar)
   {
       u = lon * DEG_TO_RAD;
       v = lat * DEG_TO_RAD;
        pj_transform(pjSrc,pjTar,1,0,&u,&v,0);
   }   
 }
 else

а второе преобразование в следующем блоке кода:

if(pjTar && pjSrc)
 {
   pj_transform(pjSrc,pjTar,1,0,&u,&v,0);
 }

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

Сменил первый блок на такой:

if(GPS_Math_Str_To_Deg(str, lon, lat,true))
 {
    u = lon * DEG_TO_RAD;
    v = lat * DEG_TO_RAD;
 }
 else

пересобрал и всё заработало отлично и замечательно.

UPD: а теперь всё то же самое, но в виде патча:

qlandkartegt-map-referencing-fix.diff
Index: src/GeoMath.cpp
===================================================================
--- src/GeoMath.cpp	(revision 3010)
+++ src/GeoMath.cpp	(working copy)
@@ -392,12 +392,8 @@
 
     if(GPS_Math_Str_To_Deg(str, lon, lat,true))
     {
-        if(pjTar)
-        {
-            u = lon * DEG_TO_RAD;
-            v = lat * DEG_TO_RAD;
-            pj_transform(pjSrc,pjTar,1,0,&u,&v,0);
-        }
+        u = lon * DEG_TO_RAD;
+        v = lat * DEG_TO_RAD;
     }
     else
     {

PS автору письмо отправлено

// Паттерны проектирования

Случайно наткнулся на сайт: http://www.design-pattern.ru - «Справочник «Паттерны проектирования»

Прицепом:

PS а это планирую купить: http://www.ozon.ru/context/detail/id/6108824

// Изменение версии в многомодульном Maven проекте

Что бы одним махом изменить версию в многомодульном проекте, достаточно использовать плагин versions, подключив его к корневому pom:

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>versions-maven-plugin</artifactId>
                <version>1.2</version>
            </plugin>

Установить версию после этого можно командой:

mvn versions:set -DnewVersion=1.0.0-SNAPSHOT

или аналогичным образом.

Подробнее:

// JBoss, JDBC: Closing a connection for you

Если узреете у себя такие исключения:

2011-07-14 17:11:14,652 INFO  [org.jboss.resource.connectionmanager.CachedConnectionManager] (http-localhost%2F127.0.0.1-8080-1) Closing a connection for you.  Please close them yourself: org.jboss.resource.adapter.jdbc.jdk6.WrappedConne
ctionJDK6@6ae0286d: java.lang.Throwable: STACKTRACE

Или около того, обратите внимание на этот документ: http://community.jboss.org/wiki/WhatDoesTheMessageDoYourOwnHousekeepingMean

А так же ознакомьтесь со следующей темой на форуме: http://community.jboss.org/thread/73267

А если коротко: если вы получаете подключение из пула, всегда обрамляйте работу с ним в конструкцию try/catch/finally примерно следующего вида:

Connection conn = null;
try
{
    // работа с базой...
}
finally
{
   try
   {
       conn.close();
   }
   catch (Throwable ignored)
   {
   }
}

// PhotoDocNG на SoftPedia

Как-то упустил сей момент, но программа PhotoDocNG, вышедшая из-под моего пера, внезапно оказалась востребованной и используемой и доброжелатель под ником Sincerely опубликовал её на софтпедии:
http://www.softpedia.com/get/Multimedia/Graphic/Digital-Photo-Tools/PhotoDocNG.shtml

Можно и по шампанскому :)

// Qt Creator Plugins Docs

По мотивам рассылки qt-creator@qt.nokia.com, подборка документации по написанию плагинов для Qt Creator'а:

Для примера можно поглядеть различные плагины на гиториусе: https://gitorious.org/search?q=qt+creator+plugins

Если будет появляться новая информация, буду дополнять пост.

// Maven trouble

Сегодня отхватили, что Maven отказался на отрез выкачивать пакеты с HTTPS репозитария JBoss, матерясь:

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Решение найдено было тут: http://blogs.sun.com/andreas/entry/no_more_unable_to_find

Если коротко:

  1. Компиляем его:
    javac InstallCert.java
  2. Запускаем рраз:
    java InstallCert repository.jboss.org

    жмахаем 1 и <RETURN>

  3. Запускаем два:
    java InstallCert repository.jboss.org

    видим что сертификат теперь trusted.

  4. Копируем файл jssecacerts в $JAVA_HOME/jre/lib/security
  5. Удаляем кеш Maven:
    rm -rf ~/.m2/repository

Всё, после этого должно всё работать.

// Image Ruler 0.98.1

Выпустил версию 0.98.1 программы Image Ruler для измерения на растровых изображениях.

Изменений минимум:

  • Добавлен вывод суммирующей длинны при измерении ломанной линией (столкнулся с расчётом маршрута по карте)
  • В win32 сборку добавлены плагины графических форматов: теперь из коробки открываются jpeg/gif и иже с ними, иначе работал только png.

// Eclipse, русский и проверка орфографии

Нужно:

  • установленный Eclipse
  • установленный aspell русский (е+ё, хотя тут на выбор) и английский словари.

Пишем скрипт, примерно такого содержимого (или выполняем команды вручную)

create-eclipse-dict.sh
#!/usr/bin/env bash
aspell dump master en      >  /tmp/ru-en.dictionary
aspell dump master ru-yeyo >> /tmp/ru-en.dictionary

получившийся файл (/tmp/ru-en.dictinary) кладём в какое-то постоянное место, например в ~/documents:

mv /tmp/ru-en.dictionary ~/documents

В запущенном Eclipse идём в Window → Preferences → General → Editor → Text Editor → Spelling (или в поиске вводим Spell, само найдётся). В секции Dictionaries параметр Platform dictionary выставляем в none, а в User defined dictionary прописываем наш получившийся словарик:

~/documents/ru-en.dictionary

Сохраняемся, закрываем настройки, пользуемся.

// Перенос строки по заданной границе без разбивки слов (word-wrap) в Java

В составе Apache Commons есть чудная библиотечка Lang в которой есть класс WordUtils который и поможет нам решить поставленную задачу, примерно следующим образом:

WordWrap.java
package test;
 
import java.io.IOException;
 
import org.apache.commons.lang.WordUtils;
 
public class WordWrap
{
  public static void main(String[] args) throws IOException 
  {
    String str = "This is a sentence that we're using to test the wrap method";
    System.out.println("Original String 1:\n" + str);
    System.out.println("\nWrap length of 10:\n" + WordUtils.wrap(str, 10));
    System.out.println("\nWrap length of 20:\n" + WordUtils.wrap(str, 20));
    System.out.println("\nWrap length of 30:\n" + WordUtils.wrap(str, 30));
 
    String str2 = "This is a sentence that we're using to test the wrap method and hereisaveryveryveryverylongword";
    System.out.println("\nOriginal String 2:\n" + str2);
    System.out.println("\nWrap length of 10, <br/>\\n newline, wrap long words:\n"
			+ WordUtils.wrap(str2, 10, "<br/>\n", true));
    System.out.println("\nWrap length of 20, \\n newline, don't wrap long words:\n"
			+ WordUtils.wrap(str2, 20, "\n", false));
  }
}

// Сортировка "цифровых" строк в Java

Для сортировки массивов в Java есть Arrays.sort(array). Есть так же форма с указанием своего компаратора.

Задача: нужно отсортировать строки, в которых содержатся цифровые значения, вроде:

bg1
bg3
bg20
bg10

После классический сортировки получим такой результат:

bg1
bg10
bg20
bg3

Не всегда это нужно, и не всегда есть возможность выравнивать нулями до формы bg01,bg03,bg30

По ссылке ниже вариант компаратора, выполняющего данную задачу: http://sanjaal.com/java/206/java-data-structure/alphanumeric-string-sorting-in-java-implementation/

// Qt Creator и CMake - продолжение

Некоторое время я поднимал тему связки Qt Creator и CMake, тогда всё показалось не очень хорошо.

В общем, собрался и сделал несколько лучше: малость допилил плагин CMakeProjectManager, реализовав следующие фичи:

  • Дерево проекта берётся не из .cbp файла, а сканированием дерева проекта. Как вариант может оказаться медленно на больших проектах, с другой стороны, релоадинг дерева происходит не каждый раз, а при смене CMakeLists.txt или при добавлении, удалении, переименовывании файлов (этого, кстати, в базовом плагине нет)
  • Теперь можно создавать новые файлы в дереве проектов непосредственно из Qt Creator'а
  • Появилась возможность переименовывать файлы
  • Появилась возможность удалять файлы с диска

Изменения оформлены в виде отдельного плагина (основано на GIT версии Qt Creator, 2.2.81) - CMakeProjectManager2 доступного на Gitorious: http://gitorious.org/hatred-qt-creator-plugins/cmakeprojectmanager2, как устанавливать - читать README.txt, написано моим дряным английским, но, в общем, должно быть понятно.

Кроме того, в основное дерево Qt Creator я подал мёрж-реквест: http://qt.gitorious.org/qt-creator/qt-creator/merge_requests/280

Кроме того, случайно наткнулся мастера новых проектов на CMake: http://apachelog.wordpress.com/2010/09/27/qt-creator-cmake-wizards/ устанавливаются просто.

// Консоль, скрипты, XML и XPath

Иногда в скриптах нужно мало-мало добраться до внутрянки XML файлов – к тегам или свойствам тегов. Для сих целей бывает удобно использовать XPath. Вопрос: а как это делать-то из скриптов?

На помощь приходит утилитка xmllint из состава libxml2. Начиная с версии 2.7.7 или 2.7.8 там появилась опция –xpath которая задаёт выражение для выборки. В более ранних версиях (2.7.6, к примеру) этой опции нет, но есть (в последних тоже) возможность вызова «консоли» и интерактивного разбора XML.

В общем два варианта:

xmllint --xpath '//File/@Name' test.xml

и

echo 'cat //File/@Name' | xmllint --shell test.xml

Остальное - фантазия.

// Qt Creator и CMake

Пакость: Qt Creator умеет импортировать CMake проекты, проблема в том, что в дереве далеко не все файлы отображаются.

Причина: делается этот импорт через откровенную задницу: вызывает cmake с генератором «-G'CodeBlocks - Unix Makefiles'», генерируя тем самым XML-файл проекта формата CodeBlocks. Но тут накладывается вторая задница: сам генератор обрабатывает файлы только для таргетов: executable, static_library, shared_library, module_library, всё остальное он забывает запихнуть в результирующий '.cbp'.

Решение

Для начала накладываем патч на CMake и пересобираем его:

cmExtraCodeBlocksGenerator.cxx.diff
--- cmExtraCodeBlocksGenerator.cxx.orig 2011-03-15 14:28:30.692010962 +1000
+++ cmExtraCodeBlocksGenerator.cxx      2011-03-15 15:01:05.612566928 +1000
@@ -410,12 +410,14 @@
     for (cmTargets::iterator ti = targets.begin();
          ti != targets.end(); ti++)
       {
+      //std::cout << "Type: " << ti->second.GetType() << std::endl;
       switch(ti->second.GetType())
         {
         case cmTarget::EXECUTABLE:
         case cmTarget::STATIC_LIBRARY:
         case cmTarget::SHARED_LIBRARY:
         case cmTarget::MODULE_LIBRARY:
+        case cmTarget::UTILITY:
           {
           const std::vector<cmSourceFile*>&sources=ti->second.GetSourceFiles();
           for (std::vector<cmSourceFile*>::const_iterator si=sources.begin();

Потом, для тех файлов, которые хотим видеть в дереве создаём фейковый таргет, примерно так:

set(script-files
    process-filelist.sh
)
# hack for display in Qt Creator (with patch for CMake)
add_custom_target(scripts true SOURCES ${script-files})

Всё, после регенерации файлы появятся в списке.

Решение костыльное, но время переписывать импортёр CMake в Qt Creator просто нет, будем надеяться, что разработчики обратят на это внимание.

// Hightide

Hightide (High Tide - большая вода) - сборка Jetty с включенными дополнительными библиотеками и сервисами JavaEE (эдакий не совсем JavaEE Application Server). Решили попробовать его для своих нужд, пока возможностей JBoss нам не нужно.

Последняя стабильная версия - 7.3.0 (поддерживается Java5), нестабильная - 8.0.0.M2 (нумерация по версии Jetty, поддерживает Java6).

Скачать можно отсюда: http://dist.codehaus.org/jetty/

Для удобной работы потребуется адаптер для WTP, который поддерживает Jetty 7 и 8: http://wiki.eclipse.org/Jetty_WTP_Plugin/Jetty_WTP_Install

Единственный нюанс: адаптер рассчитан чисто на Jetty, поэтому в дефолтной конфигурации просто не запустится - не будет хватать jar-ников. Пока выход нашёл ровным счётом один: по двойному клику на вкладке Servers по Jetty открываем диалог конфигурации, там идём в «Open launch configuration» и далее двигаемся на вкладку Classpath и в категории User Entries добавляем External JARs, которые ищем в lib директории и поддиректориях в jetty.home.

Если есть другие пожелания - в каментах жду ответов.

При работе с Maven поможет эта статья: http://docs.codehaus.org/display/JETTY/Fast+WebApps+with+Hightide

А про мой «любимый» JAAS почитать можно тут: http://docs.codehaus.org/display/JETTY/JAAS, а вообще по документации:

По вопросам горячего деплоя можно почитать тут: http://phishbouncer.com/trac/browser/trunk/jetty/demo/webapps/jetty/faq/HotDeploy.html, где сказано, цитирую:

Q: Can Jetty Hot Deploy?

A: Jetty is fully hot deployable, but does not have any native hot deployers.
What this means is that Jetty contexts, servlets and filters may be added, stopped,
started and reloaded dynamically, but that Jetty does not have any services that
trigger these events.

Далее по тексту идут причины такого поведения. Ну и полезным по этой теме будет:

Пока реализовывать не пробовал. Будут результаты - будет отчёт.

// Си, прекрасное

http://eli.thegreenplace.net/2011/02/15/array-initialization-with-enum-indices-in-c-but-not-c/ - это воистину прекрасно! Использовать enum для именованной инициализации массива. Можно делать в C99, не работает в C++

Ну и ссылки, на правах мемориза:

  • http://www.advancedlinuxprogramming.com/alp-folder - по названию сайта уже понятно, что это такое :) книжка «Advanced Linux Programming» в сободном доступе. На просторах интернета встречается русский перевод, можно поискать.
  • http://mij.oltrelinux.com/devel/unixprg/ - «UNIX programming examples» - примеры кода на Си для unix, в частности - сокеты, IPC, потоки.

// Apache Maven 2

На правах мемориза:

// Maven и WTP

WTP - Eclipse Web Tools Platform. Набор расширений для удобной работы с WEB проектами. Деплой на различные серверы приложений, отладка, профилирование и много всего другого вкусного.

Про Maven не пишу, многим понятно. В последних версиях плагина для Eclipse в extra есть модули для j2ee проектов различных версий соглашений (j2ee 5/6 и младше). Но статья по ссылке:
http://www.devx.com/Java/Article/36785

будет полезна как инструкция по переводу существующего проекта, созданного средсвами Eclipse и WTP на сборку maven'ом.

// Java и LDAP. Малость заметок

Помаленьку, понемножку, на уровне ссылок.

Заметка первая: LDAP как JNDI ресурс в context.xml

Читать тут: http://old.nabble.com/LDAP-as-resource-in-context.xml-td14204908.html

Из бяк: при использовании пула подключений, само подключение создаётся в момент

new InitialDirContext(env);

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

Заметка вторая: использование DN в фильтрах

Не сразу прокурил как, вот статейка на русском языка, поясняющая - как это нужно делать: http://sysadminblog.ru/ldap/2011/01/18/tipichnye-primery-ispolzovaniya-entrydn-v-ldap-filtrah.html

// ThreadLocal Memory Leak

Использовать ThreadLocal переменные удобно, для расшаривания данных между разными частями одного потока (допустим достучаться из POJO объекта к HTTP сессии и т.п.). Другое дело, что чревато в контексте использования сервера приложений: тут треды не уничтожаются, а возвращаются в пул потоков, как следствие: GC не собирает их, они остаются в памяти, а тут, помимо явной утечки, есть ещё и потенциальная дырка в безопасности.

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

По этой ссылке:
http://wiki.apache.org/tomcat/MemoryLeakProtection
Можно почитать про разные MemLeaks в веб-приложениях (точнее про технологию защиты от оных в Tomcat)

UPD: подкатом решение (???)

Решение

Найдено тут: http://weblogs.java.net/blog/jjviana/archive/2010/06/09/dealing-glassfish-301-memory-leak-or-threadlocal-thread-pool-bad-ide

какое выбирать, решать вам, я выбрал второй вариант с наследованием класса. Скопирую его тут с форматированием:

import java.lang.ref.SoftReference;
 
public abstract class SoftThreadLocal<T> extends ThreadLocal<T>
{
    // Encapsulation required because Generics is stupid about references, and there is no interface for ThreadLocal, 
    //so I can't simply extend a single ThreadLocal, grr! Java has so many brittle design mistakes in it.
    private final ThreadLocal<SoftReference<T>> local = new ThreadLocal<SoftReference<T>>();
 
 
    @Override
    public T get() 
    {
        SoftReference<T> ref = local.get();
        T result = null;
 
        if (null != ref) 
        {
            result = ref.get();
        }
 
        if (null == result) 
        {
            result = initialValue();
            ref = new SoftReference<T>(result);
            local.set(ref);
        }
 
        return result;
    }
 
 
    @Override
    public void set(T value) 
    {
        if (null == value) 
        {
            remove();
        } 
        else 
        {
            local.set(new SoftReference<T>(value));
        }
    }
 
 
    @Override
    public void remove() 
    {
        local.remove();
    }
}

Далее работа классическая, к примеру, как у меня через ThreadLocal Singleton:

import java.util.HashMap;
 
/**
 * Небольшой вспомогательный статический класс
 * для расшариванить локальных для треда переменных. Значение будет для каждого треда своё.
 * 
 * Допустим, получая в методе doPost()/doGet() сервлета значение сессии и для проброса во все дочерние
 * классы, без необходимости передачи оных параметром, что, собственно говоря, не всегда возможно.
 *
 * @author hatred
 * 2011-01-27
 */
public class ThreadContext
{
    private /*static*/ final SoftThreadLocal<HashMap<Object, Object>> _context = new SoftThreadLocal<HashMap<Object, Object>>()
    {
        protected HashMap<Object,Object> initialValue()
        {
            return new HashMap<Object,Object>();
        }
    };
 
    private static ThreadContext _instance = null;
    //
 
    synchronized public static ThreadContext getContext()
    {
        if (_instance == null)
        {
            _instance = new ThreadContext();
        }
 
        return _instance;
    }
 
    synchronized public static void releaseContext()
    {
        if (_instance != null)
        {
            _instance.clear();
            _instance.contextRemove();
        }
 
        _instance = null;
    }
 
    public void put(Object key, Object value)
    {
        _context.get().put(key, value);
    }
 
    public Object get(Object key)
    {
        return _context.get().get(key);
    }
 
    public void remove(Object key)
    {
        _context.get().remove(key);
    }
 
    public void clear()
    {
        _context.get().clear();
    }
 
    protected void contextRemove()
    {
        _context.remove();
    }
}

Кстати, не могу понять, насколько правильно делать getContext() и releaseContext() synchronized?

// JAAS in Action

Довелось повозиться с JAAS, для организации управления доступом внутри XML-RPC сервиса (реализованного в виде сервлета, крутящегося внутри Tomcat). В результате поиска хорошей, систематизированной информации, наткнулся на книжку: JAAS in Action (http://jaasbook.wordpress.com/)

Книжка доступна для свободного скачивания в формате PDF, лицензия - CC.

Как результат, я её крайне рекомендую: содержит всё необходимое для вникание в работу с JAAS, содержит практические примеры, среди которых, к примеру, использование нескольких Policy-источников, показаны примеры для хранения настроек JAAS, аутентификационной (имя пользователь и пароль, группы пользователя) и авторизационной (политики, разрешения) информации в базе (легко разворачивается до использования других хранилищ, к примеру - LDAP). Язык английский, но читается крайне легко.

Другие ссылки, касательно JAAS, можно поглядеть тут.

P.S. эпопею с сервлетом и авторизацией, я ещё опубликую, как только полностью закончу.

// Новый программерский жаргон

Запостил камрад AlexVK (1161371):

Программистский жаргон. Раз: http://habrahabr.ru/blogs/habraquotes/111432/

И далее, по ссылке два http://habrahabr.ru/blogs/development/96978/

И первоисточник три http://stackoverflow.com/questions/2349378/new-programming-jargon-you-coined

// Генератор анимационных индикаторов ожидания

Для всякого непотребства, свазанного с ожиданием:
http://ajaxload.info/

// Astxx: action для команды QueueStatus (+пример)

Логическое продолжение для этой статьи, команда для получения состояния очереди.

Подкатом реализация и пример использования команд QueuePause и QueueStatus, какие эвенты получаются смотрите в документации1)

Реализацяи QueueStatus

queue_status.h
/* 
 * Additonal actions for Astxx
 * Copyright (C) 2010  Alexander Drozdov <hatred@inbox.ru>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
/** @file
 *
 * Include this file to use the QueueStatus action. You should include it by hands.
 *
 */
 
#ifndef ASTXX_MANAGER_ACTION_QUEUE_STATUS_H
#define ASTXX_MANAGER_ACTION_QUEUE_STATUS_H
 
#include <astxx/manager/basic_action.h>
#include <astxx/manager/message.h>
#include <astxx/manager/error.h>
 
namespace astxx {
   namespace manager {
      namespace action {
         /** Ask Queue Status
          */
         class queue_status : public basic_action {
            public:
               /** Construct a QueueStatus action.
                * @param queue          - concretize queue [optional]
                * @param member         - select member [optional]
                * @param action_id      - add optional action Id to all responses [optional]
                */
               queue_status(const std::string& member    = "",
                            const std::string& queue     = "", 
                            const std::string& action_id = "") :
                                member(member),
                                queue(queue),
                                action_id(action_id) {}
 
               /** Format this action as a message::action.
                * @return this message as a message::action
                */
               message::action action() const {
                  message::action action("QueueStatus");
 
                  if (!queue.empty())
                  {
                    action["Queue"] = queue;
                  }
 
                  if (!member.empty())
                  {
                    action["Member"] = member;
                  }
 
                  if (!action_id.empty())
                  {
                    action["ActionID"] = action_id;
                  }
 
                  return action;
               }
 
            private:
               std::string queue;
               std::string member;
               std::string action_id;
         };
      }
   }
}
 
#endif // QUEUESTATUS_H

Пример

Пример основан на event-test.cc, что идёт вместе с astxx.

queue-event-test.cpp
#include <astxx/manager.h>
#include <vector>
#include <string>
#include <iostream>
 
#include "queue_status.h"
#include "queue_pause.h"
 
void print_event(astxx::manager::message::event e) 
{
   //std::cout << e.format();
   std::cout << e["Paused"] << "\n";
}
 
int main(int argc, char** argv) 
{
   std::vector<std::string> args(argv, argv + argc);
 
   if (args.size() != 4) 
   {
      std::cerr << "Usage: " << args[0] << " [host] [username] [secret]\n";
      return 1;
   }
 
   try 
   {
      namespace manager = astxx::manager;
      namespace action = astxx::manager::action;
 
      manager::connection connection(args[1]);
      std::cout << "Connected to " << connection.name() << " v" << connection.version() << std::endl;
 
      boost::signals::scoped_connection c1 = connection.register_event("QueueMember", print_event);
      //boost::signals::scoped_connection c2 = connection.register_event("Queue", print_event);
 
      action::login login(args[2], args[3]);
      login(connection);
 
      action::queue_pause qpause("SIP/3008", true);
      manager::message::response resp = qpause(connection);
      std::cout << resp.format();
 
      action::queue_status qstatus("SIP/3008");
      resp = qstatus(connection);
      std::cout << resp.format();
 
      action::queue_pause qpause2("SIP/3008", false);
      resp = qpause2(connection);
      std::cout << resp.format();
 
      action::queue_status qstatus2("SIP/3008");
      resp = qstatus2(connection);
      std::cout << resp.format();
 
 
      for (;;) 
      {
         connection.wait_event();
         connection.pump_messages();
         connection.process_events();
      }
      return 0;
   }
   catch (const std::exception& e) 
   {
      std::cerr << "Exception: " << e.what() << std::endl;
   }
   return 1;
}

Собственно что тут происходит:

  • создаём подключение
  • регистрируем обработчик для эвента QueueMember (смотрим токи на AMI)
  • логинимся
  • ставим мембера в паузу и спрашиваем его состояние
  • убираем паузу и опять спрашиваем его состояние
  • в цикле обрабатываем приходящие эвенты
1)
тут или тут, вторая ссылка более полная, но на буржуйском, хотя кого это пугает? ;-)

// Astxx: action для команды QueuePause

Парой слов, что такое Astxx небольшая библиотечка для работы с Asterisk через AMI протокол из C++. Написана с использование STL и Boost.

В одной из прошлых заметок упоминал про дружбу Qt4 и Boost.Signal - для дружбы этой библиотечки и Qt4 это и было нужно :)

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

Сами классы подкатом.

UPD Переоформил в стиле Astxx

queue_pause.h
/* 
 * Additonal actions for Astxx
 * Copyright (C) 2010  Alexander Drozdov <hatred@inbox.ru>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
/** @file
 *
 * Include this file to use the QueuePause action. You should include it by hands.
 *
 */
 
#ifndef QUEUE_PAUSE_H
#define QUEUE_PAUSE_H
 
#include <astxx/manager/basic_action.h>
#include <astxx/manager/message.h>
#include <astxx/manager/error.h>
 
#include <vector>
#include <string>
 
namespace astxx
{
    namespace manager
    {                                                                                                                                                                                                                                        
        namespace action                                                                                                                                                                                                                     
        {                                                                                                                                                                                                                                    
            /** Set Queue Pause state                                                                                                                                                                                                        
            */                                                                                                                                                                                                                               
            class queue_pause : public basic_action                                                                                                                                                                                          
            {                                                                                                                                                                                                                                
            public:                                                                                                                                                                                                                          
                class error : public manager::error                                                                                                                                                                                          
                {                                                                                                                                                                                                                            
                public:                                                                                                                                                                                                                      
                    explicit error(const std::string& desc) throw() : manager::error(desc) { }                                                                                                                                               
                };                                                                                                                                                                                                                           
 
            public:                                                                                                                                                                                                                          
                /** Construct a QueuePause action.
                * @param interface      - member
                * @param is_on          - pause state
                * @param queue          - queue for process, if omited - all queues is used
                */
                queue_pause(const std::string &interface, const bool is_on = false, const std::string &queue = "") :
                    _interface(interface),
                    _queue(queue),
                    _is_on(is_on) {}
 
               /** Format this action as a message::action.
                * @return this message as a message::action
                */
                message::action action() const
                {
                    message::action action("QueuePause");
 
                    if (!_queue.empty())
                    {
                        action["Queue"] = _queue;
                    }
 
                    action["Interface"] = _interface;
 
                    std::string on_off_str = "false";
                    if (_is_on)
                    {
                        on_off_str = "true";
                    }
 
                    action["Paused"] = on_off_str;
 
                    return action;
                }
 
                message::response handle_response(message::response response)
                {
                    basic_action::handle_response(response);
                    if ("Success" != response)
                    {
                        throw queue_pause::error(response["Message"]);
                    }
 
                    return response;
                }
 
            private:
                std::string _interface;
                std::string _queue;
                bool        _is_on;
            };
 
        }
    }
}
 
#endif // QUEUE_PAUSE_H

// Qt4: поместить окно на все рабочие столы

Встроенного функционала нет, для X11 можно записпользовать такой код (юзается XLib):

#ifdef Q_WS_X11
#  include <X11/Xlib.h>
#  include <X11/Xatom.h>
#endif
 
...
 
toAllDesktops()
{
#ifdef Q_WS_X11
    Atom atom = XInternAtom(x11Info().display(), "_NET_WM_DESKTOP", True);
    if (atom)
    {
        uint32_t data = 0xFFFFFFFF;
        XChangeProperty(x11Info().display(),                // Display
                        winId(),                            // Window
                        atom,                               // Property
                        XA_CARDINAL,                        // Property type
                        32,                                 // Data format: 8, 16 or 32 bit
                        PropModeReplace,                    // Property change mode
                        reinterpret_cast<uint8_t*>(&data),  // Property data
                        1);                                 // Data elements count
    }
#endif
}

тут предполагается, что эта функцейка будет медотом класса окна, иначе нужно будет передавать Display и Window. Специальное значение 0xFFFFFFFF как раз и говорит - разместить на всех рабочих столах, иначе воспринимается как номер стола, куда нужно поместить окно. В случае Windows этот функционал бесмесленен, поэтому портабельность не сильно пострадает, при использовании директив препроцессора.

Подробности:

// Boost.Signals и Qt4

Использую библиотечку astxx, которая использует Boost.Signals, использую в программе на Qt, сразу словил ошибку компиляции:

g++ -c -m64 -pipe -g -Wall -W -D_REENTRANT -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt/mkspecs/linux-g++-64 -I../queue-test -I/usr/include/QtCore -I/usr/include -I. -I../queue-test -I. -o main.o ../queue-test/main.cpp
In file included from /usr/include/boost/signals/connection.hpp:13:0,
                 from /usr/include/boost/signals/signal_template.hpp:18,
                 from /usr/include/boost/signals/signal0.hpp:24,
                 from /usr/include/boost/signal.hpp:19,
                 from /usr/include/astxx/manager/connection.h:40,
                 from /usr/include/astxx/manager.h:32,
                 from ../queue-test/main.cpp:8:
/usr/include/boost/signals/detail/signals_common.hpp:26:13: error: expected identifier before ‘protected’
/usr/include/boost/signals/detail/signals_common.hpp:26:13: error: expected unqualified-id before ‘protected’
../queue-test/main.cpp:75:1: error: expected ‘}’ at end of input
make: *** [main.o] Error 1

Описания способов решения нашел тут:
http://www.boost.org/doc/libs/1_45_0/doc/html/signals/s04.html

Пока же, временно, определил макрос:

#define BOOST_SIGNALS_NAMESPACE boost_signals

Upd:
В .pro файле добавил:

CONFIG += no_keywords

После чего такие конструкции перестанут компилироваться:

signals:
private slots:
public slots:

Заменить на:

Q_SIGNALS:
private Q_SLOTS:
public Q_SLOTS:

// Java Enums

Для тех случаев когда enum нету, к примеру, при использовании BeanShell:
http://alumnus.caltech.edu/~leif/OO/Enum.html

Подкатом небольшое дополнение и пример.

Небольшая ремарка, для BeanShell придется разносить класс и сами создания констант, к примеру так:

enum.java
// Не обязательно
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
 
/**
 Хранит описание одной записи в дополнительных действиях, а так же собирает их в перечисление
 дабы можно было использовать интерфейс коллекций для перебора при формировании списка
*/
class AdditionalActionId
{
    private static Collection all_ids = new ArrayList();
 
    private int    value;
    private String description;
 
    /**
     Конструктор, принимает ID дополнительного действия и описание (что будет в меню отображаться)
    */
    AdditionalActionId(int value, String description)
    {
        this.value       = value;
        this.description = description;
 
        all_ids.add(this);
    }
 
    /**
     Возвращает Id действия
    */
    public int toInt()
    {
        return this.value;
    }
 
    /**
     Возвращает описание действия
    */
    public String toString()
    {
        return this.description;
    }
 
    /**********************************************************************************************/
    /* Statics methods (class wide methods) ------------------------------------------------------*/
    /**********************************************************************************************/
 
    /**
     Получаем итератор для перебора всех ранее созданных действий
    */
    public static Iterator getIterator()
    {
        return all_ids.iterator();
    }
 
    /**
     Получаем количество дополнительных действий
    */
    public static int getSize()
    {
        return all_ids.size();
    }
}
 
/**
 Класс-обертка, для реализации некоторого подобия Enum (который не работает в BeanShell)
*/
class AdditionalActionIds
{
    private static int action = 1;
 
    // Перечисляем наши действия
    //public static final AdditionalActionId TARIFF_ADD_COMMON1 = new AdditionalActionId(action++, "TEST");
    //public static final AdditionalActionId TARIFF_ADD_COMMON2 = new AdditionalActionId(action++, "TEST");
    //public static final AdditionalActionId TARIFF_ADD_COMMON3 = new AdditionalActionId(action++, "TEST");
    //public static final AdditionalActionId TARIFF_ADD_COMMON4 = new AdditionalActionId(action++, "TEST");
 
    // Must be LAST
    public static final AdditionalActionId ABON_RECALC_ALL    = new AdditionalActionId(1000000, "Дополнительное действие");
}
 
// Пример перебора всех значений
/*Iterator it = AdditionalActionId.getIterator();
while (it.hasNext())
{
    action = (AdditionalActionId)it.next();
    print("ID: " + action.toInt() + ", desc: " + action.toString());
}
*/
 
// Пример обращения к конкретному элементу
/*AdditionalActionId action = AdditionalActionIds.TARIFF_ADD_COMMON3;
print("ID: " + action.toInt() + ", desc: " + action.toString());*/

Иначе имеем проблемы. Не знаю, бага или фича, но факт.

// Шрифты для кода

Хороший шрифт, который не ломает глаза, не вносит неоднозначности (банальная проверка на визуальную отличимость O от 0 (тут, к примеру, многими любимый Droid сливает), 1 от I и от l (единица/i-большая/L-маленькая), 5 от S, по крайней мере это первое, на что я внимание обращаю) бережёт нервы и зрение при правке и чтении/анализе кода.

В Linux неплохим выбором будут шрифты Bitstream Vera Sans Mono или DejaVu Sans Mono, тем более, что в моём любимом ArchLinux они есть в репозитории extra. Но не всем они подходят, или иногда хочется чего-то новенького :)

В общем, конкретики давать не буду, дам ссылочки, по которым стоит посмотреть и подобрать себе подходящий (ещё ссылки приветствуются, в т.ч. и на отдельные шрифты, которые, по вашему мнению, удобны для кода).

Напомню, что в Linux TTF шрифты достаточно поместить в каталог ~/.fonts/ и выполнить команду fc-cache в терминале (не от root'а)

Итак, коллекции конкретно программерских шрифтов:

  1. https://github.com/chrissimpkins/codeface - богатая коллекция + каталог шрифтов для кода. Естественно доступны все инструменты гитхаба, типа форкнуть, подправить и подать свой мерж-реквест (ссылку подкинули в одном из обсуждений на просторах рунета).
  2. http://www.lowing.org/fonts/ - Monospace/Fixed Width Programmer's Fonts
  3. http://www.proggyfonts.net/ - Monospaced Bitmap Programming Fonts (альтернативные ссылки: раз, два)

Отдельные шрифты:

  1. Inconsolata
    • шрифт, как заявлено, для нормального отображения как на экране, так и на печати, схож с TheSansMono (в общем смотрите на сайте)
  2. Inconsolata LGC
  3. Anonymous Pro и Anonymous
  4. Monaco с русскими глифами
  5. Anka/Coder, автор русский, можно пожелания на родном языке писать, шрифт нуждается в доводке, хорошее отображение удалось получить только в Linux (в первую очередь ориентирован на печать)
  6. Meslo LG (кириллица есть)
  7. Fira Sans, включая Fira Mono (кириллица есть)
  8. Hack (кириллица есть, ноль с вертикальной чертой)
  9. Input Mono (посоветовали в комментах)

Ну а кому нужны шрифты с программерскими лигатурами (составные символы типа ==, ===, != и т.д., скрины с начертанием есть по ссылкам):

  1. Fira Code, основан на Fira Sans:
  2. Hasklig, основан на Source Code Pro (больше подходит для хаскеля и прочей функциональщины)

А это просто коллекция свободных шрифтов:

Статьи и обзоры:

  1. Статья на хабре, с примерами начертания: http://habrahabr.ru/post/120316/ (и, судя по всему, англоязычный оригинал: http://hivelogic.com/articles/top-10-programming-fonts/)
  2. Обзор разных шрифтов: http://www.codeproject.com/KB/work/FontSurvey.aspx
  3. Моноширинные шрифты с программистскими лигатурами. Лично я против подобных модификаций, есть куча нюансов в последующем чтении кода, но там, по ходу пьесы, пролетели ссылки на интересные шрифты (выше список пополнился).

Мой выбор:

  • Anka/Coder - юзал бы его, если не косяки. А так - нравится
  • Meslo LG
  • Fira Mono
  • Anonymous Pro

UPD: убрал все прочие UPD и переформатировал статью

// Crowns 0.6.0

Выпустил версию 0.6.0 программы Crowns.

Из основных изменений:

  • Добавлена возможность задавать ограничение на отображение деревьев по возрасту (кстати это поле, как я понял, в основном используется не для возраста, а для задания года «рождения» дерева)
  • Исправлен диалог редактирования данных: в нем невозможно было задать возраст дерева

// MountTray

Написал небольшую программу для подключения, отключения сменных дисков в Linux, использует udisks для монтирования (в планах сделать бакенд и для простых mount/unmount через sudo, класс для использования которого уже написан, для полных минималистов), udev для детекта новых дисков и изъятия существующих (опять таки, готов и бакед для использования inotify, можно будет альтернативно прикрутить его, кстати, а на други unix системах как с inotify?) и в минимальном плане DBus для возможности получать сообщения: а вдруг кто-то там снаружи примонтировал/отмонтировал диск.

Пожелания - мне на мыло

Проект разместил на Gitorius: http://gitorious.org/h4tr3d-utils/pages/MountTray
Новый адрес: https://github.com/h4tr3d/mount-tray, проект не разрабатывается уже несколько лет.

// QFileDialog с Image Preview: метод хакера

В Qt3 была чудная возможность в диалоге открытия интегрировать виджет в котором осуществлять предпросмотра содержимого файла, в частность делать предпросмотр изображений. В Qt4 такую возможность убрали. Да, можно приделать при помощи ItemDelegate кастомное отображение для иконок, и тем самым осуществлять предпросмотр, но не всегда это бывает удобным. Следующий вариант: писать свою реализацию класса для диалога открытия/сохранения. Я же решил попробовать хитрый способ…

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

Итак смыл: наследовать свой класс от QFileDialog, в конструкторе при помощи findChild() по имени искать контейнер в котором располагается виджеты для отображения иконок и быстрых ссылок, приводить его к нужному типу, создавать виджет для предпросмотра, добавлять его в контейнер, при смене файла обновлять содержимое.

Для того, что бы узнать какой контейнер используется (что бы произвести приведение типа) и его имя, нам потребуются исходники Qt, которые скачиваем с сайта Nokia: http://qt.nokia.com, распаковываем, находим файл qfiledialog.ui и загружаем его в Qt Designer. После пары минут изучения находим, что контейнер это компонент типа QSplitter и имя у него - splitter. Всё просто и лаконично.

Теперь создаём свой класс, наследуем его от QFileDialog

class MFileDialog : public QFileDialog

В теле класса обязательно ставим

Q_OBJECT

в секции private: создаем прототип функции init() - её будем вызвать из конструктора (если вы решитесь объявить их несколько), и указатель на экземпляр класса QLabel - его будем использовать для вывода миниатюры изображения:

private:
    void init();
    QLabel *_preview;

Да, я обычно использую для методов класса одну секцию private/public/etc, а для переменных - другую. Так получается нагляднее.

Далее, сразу вспоминаем, что нам нужно обновлять предпросмотр при смене файла, для этого создадим слот - обработчик сигнала currentChanged(QString):

public slots:
    void fileChanged(const QString &file);

Реализации методов init() и fileChanged(QString &file) в листинге ниже:

void MFileDialog::init()
{
    _preview = 0;
 
    // HACK: original file dialog in Qt4 does not have preview functinality
    // Work well on 4.7.1
    QSplitter *splitter = findChild<QSplitter*>("splitter");
    if (splitter == 0)
    {
        return;
    }
 
    _preview = new QLabel();
    QRect geomerty = _preview->geometry();
    geomerty.setWidth(160); // Настройки геометрии, можно так же сделать что бы рамочка отображалась или что-то вроде 
    _preview->setGeometry(geomerty);
 
    splitter->addWidget(_preview); // Собсвенно, добавляем наш виджет в контейнер
 
    // подключаем обработчик сигнала currentChanged
    connect(this, SIGNAL(currentChanged(QString)),
            this, SLOT(fileChanged(QString)));
}
 
void MFileDialog::fileChanged(const QString &file)
{
    if (_preview == 0)
    {
        return;
    }
 
    QPixmap pix(file);
    if (pix.isNull())
    {
        _preview->setPixmap(QPixmap());
    }
    else
    {
        // Не зыбываем масштабировать изображение к размеру нашей области просмотра 
        QSize size = _preview->size();
        pix = pix.scaled(size, Qt::KeepAspectRatio);
        _preview->setPixmap(pix);
    }
}

Не забываем в конструкторе вызвать init(), а в деструктор поместить delete _preview. И всё, примерно так это выглядит:

При этом, если даже не будет найдет разделитель с таким именем, диалог не поломается - просто не будет отображаться окошко предпросмотра.

Какие улучшения тут можно сделать? Я думаю как минимум такие:

  1. Установление ограничение на размер файла для которого будет автоматически делаться предпросмотр, ибо загрузка занимает время, а файлы могут быть оооочень большими (у меня, к примеру, есть такие которые не отрываются по причине нехватки памяти).
  2. Вместо QLabel использовать QToolButton и если файл слишком большой отрабатывать нажатие и делать принудительный предпросмотр, либо какие-то другие действия.

PS Кому нужны исходники, пишите на мыло.

// Qt4 и Custom Types

Известно, что, без преувеличения, основа Qt это moc и их система метатипов. В базе метатипов зарегистрированы все простые C/C++ типы и все сложные Qt типы, часто этого бывает с головой достаточно для написания программ: при использовании QVariant и QSettings или при организации вызовов типа сигнал-слот…

Но бывает, что в качестве параметра сигнала нужно передавать свою структуру или класс, или преобразовывать свой тип в QVariant. Для этого нужно зарегистрировать свой тип перед первым использованием (можно в функции main()), примерно так:

qRegisterMetaType<PhotoFormat>("PhotoFormat");

После этого его можно использовать в качестве параметра в механизме сигнал-слот.

Следующим полезным шагом - приведение нашего типа к QVariant, для этого, в дополнение к предыдущему, в заголовочном файла где располагается объявление нашего типа (на самом деле - в любом месте, но так будет логичнее) нужно разместить такую конструкцию:

Q_DECLARE_METATYPE(PhotoFormat);

Всё, после этого можно использовать подобные конструкции:

QVariant var = qVariantFromValue(PhotoFotmat("Name", 23, 22));
PhotoFormat format = qVariantValue<PhotoFormat>(var);

Ну и последнее, сохранение и восстановление нашего класса при помощи QSettings. Для этого нужно, для начала, зарегистрировать потоковые операторы:

qRegisterMetaTypeStreamOperators<PhotoFormat>("PhotoFormat");

Далее, обычно в заголовочном файле, где объявлен класс, объявляются операторы « и »:

QDataStream &operator<<(QDataStream &out, const PhotoFormat &obj);
QDataStream &operator>>(QDataStream &in, PhotoFormat &obj);

Обращаю внимание - за пределами class {};

А реализация примерно такая:

QDataStream & operator <<(QDataStream &out, const PhotoFormat &obj)
{
    QString name = obj.getFormatName();
    QSizeF  size = obj.getSize();
    qreal   dst1 = obj.getTopPateDistance();
    qreal   dst2 = obj.getNoseChinDistance();
 
    out << name << size << dst1 << dst2;
    return out;
}
 
QDataStream & operator >>(QDataStream &in, PhotoFormat &obj)
{
    QString name;
    QSizeF  size;
    qreal   dst1;
    qreal   dst2;
 
    in >> name >> size >> dst1 >> dst2;
 
    obj.setFormanName(name);
    obj.setSize(size);
    obj.setTopPateDistance(dst1);
    obj.setNoseChinDistance(dst2);
 
    return in;
}

По сути, сериализация класса. Более подробная информация - на странице справки по классу QMetaType: qthelp://com.trolltech.qt.470/qdoc/qmetatype.html и можно поглядеть тут: http://www.crossplatform.ru/?q=node/281

// Qt4 on Mac OS X

Делал тут небольшую работку, писал программку, которая позволяла таскать линии по холсту и формировать по его данным потом xml файл в заданном формате, который потом превращался в уровень для игры. Заказчик просил ObjectivC и целевая платформа Mac OS X, на мой вопрос о критичности ObjectvC ответил - не суть, предложил написать на Qt4, тем самым покроем, одним махом, три платформы: Linux/Windows/Mac, но будет нуанс - я только примерно знаю как собирать под Mac, в общем, подумав - согласились.

Работу, естественно, я делал под Linux, проверил сборку для Windows (опять таки - кросскомпиляция). Показал результаты (сборку под Win32 и скапченый видеоролик работы в Linux), заказчику понравилось, дальше пошла очередь запуска на Mac OS X…

Первым делом был скачан Qt SDK 2010.05 - бронебойно, но зато всё сразу есть. Собрать получилось без проблем (были ворнинги у линковщика на лишние пути, указанные через атрибут -L, которые реально не существуют, но тут камень в огород создателям SDK). А вот дальше встал вопрос - а как запускать приложение на машинах где нет Qt4? Под windows/linux, в случае распространения бинарников, можно просто положить необходимые dll/so рядом и исполняемым файлов, но в Mac OS X они распространяются в виде бандла (директория с определённой структурой и суффиксом .app). Так вот, что бы собрать всё нужные библиотеки в бандл, разработчики Qt сделали утилиту macdeployqt, которую, после сборки, достаточно натравить на полученное приложение так:

macdeployqt helloworld.app

Тут человек с ником Ayoy, тоже озадачивался подобными вопросами, и рассмотрел более подробно. Так же, он столкнулся с тем, что если используются библиотеки/фреймворки которые используют Qt, после обработки macdeployqt они будут продолжать ссылаться на системные, а не те, что в бандле. Что бы это исправить он написал скрипт на перл, которые позволяет это исправлять: http://gist.github.com/109674

Чуть позже поставлю в виртуалку Mac OS X, посмотрю, может на будущее буду сам делать бандлы, а ещё заинтересовала кросс-компиляция для макоси :)

Да, в Qt Assistant страница посвященная Mac: qthelp://com.trolltech.qt.470/qdoc/deployment-mac.html версию естественно ставим свою.

// Документация по GCC

На правах мемориза.

Тут лежит полная и последняя: http://gcc.gnu.org/onlinedocs/ в различных форматах, берём то, что нужно, а в системе пользуемся инфо

А здесь полезная информация от гентоводов: http://en.gentoo-wiki.com/wiki/Safe_Cflags - безопасные флаги оптимизация для различных процессоров (далее по тексту - ссылки)

// Цветовое пространство YUV

Многим знакомо цветовое пространство RGB (Red/Green/Blue), мне потребовалось же работать с входными данными пространства YUV, которое широко используется в семействе кодеков MPEG.

// На правах заметки Using Visual C++ DLLs with C++Builder

Потребовалось тут собрать для C++Builder DLL, наткнулся походу на такой мануал:
http://bcbjournal.org/articles/vol4/0012/Using_Visual_C_DLLs_with_CBuilder.htm

В мемориз, хотя, надеюсь, оно мне никогда не пригодится, собирать под винду, это ужас…

// 256

Ну чтос, с праздничком!

// Гоняем структуры по сети, часть вторая

Тут уже писал про проблему пересылки/приёмки структурированных данных по сети.

На нашей целевой платформе нельзя применять сторонние библиотеки (типа libpack, как в предыдущей статье), и, так вышло (лучи ненависти), что #pragma pack() то работает, то не работает, да ещё стоит условие, что данные должны быть преобразованы к сетевому виду (ntohs(), ntohl(), htons(), htonl()), поэтому я сделал на макросах такой фит ушами:

/**************************************************************************************************/
/* Паковка и распаковка данных, для передачи по сети ---------------------------------------------*/
/**************************************************************************************************/
 
// Распаковка массива в структуры
/**
 Начало блока распаковки массива данных, на которые указывает \c ptr и размера \c size
 */
#define unpack_begin(ptr, size) { \
            size_t   __addr = 0; \
            size_t   __size = (size_t)(size); \
            uint8_t *__ptr  = (uint8_t*)(ptr); \
            union { \
                uint8_t        cc[8]; \
                uint16_t       ii[4]; \
                uint32_t       ll[2]; \
                float          ff[2]; \
                double         dd;    \
            } __swapd; (void)(__swapd)
 
/**
 Распаковка очередной порции данных в \c target
 */
#define unpack_next(target) \
            pack_printf("Unpack \"%s\"... Size: %d, current offset: %d, current size: %d\n", #target, __size, __addr, sizeof(target)); \
            if ((__addr + (int)sizeof(target)) <= (__size)) \
            { \
                memcpy(&(target), __ptr + __addr, sizeof(target)); \
                __addr += sizeof(target); \
            } (void)(0)
 
/**
 Распаковка очередной порции данных в \c target, с преобразованием из сетевого вида
 Сетевое преобразование работает для простых типов размерос 2 (short), 4 (int, float)
 и 8 (lint32_tlint32_t double) байтint16_t остальных данных
 */
#define unpack_net_next(target) \
            unpack_next(target); \
            switch(sizeof(target)) \
            { \
                case 2: \
                    memcpy(&__swapd.ii[0], &target, 2); \
                    __swapd.ii[0] = ntohs(__swapd.ii[0]); \
                    memcpy(&target, &__swapd.ii[0], 2); \
                    break; \
                case 4: \
                    memcpy(&__swapd.ll[0], &target, 4); \
                    __swapd.ll[0] = ntohl(__swapd.ll[0]); \
                    memcpy(&target, &__swapd.ll[0], 4); \
                    break; \
                case 8: \
                    memcpy(&__swapd.dd, &target, 8); \
                    __swapd.ll[0] = ntohl(__swapd.ll[0]); \
                    __swapd.ll[1] = ntohl(__swapd.ll[1]); \
                    memcpy(&target, &__swapd.dd, 8); \
                    break; \
            } (void)(0)
 
/**
 Конец блока распаковки
 */
#define unpack_end() } (void)(0)
 
 
// Паковка данных из элементов струтуры в массив данных
/**
 Начало блока паковки данных в \c ptr размером \c size
 */
#define pack_begin(ptr, size) unpack_begin(ptr, size)
 
// Внутренний вспомогательный макрос, как общая часть для pack_next()/pack_net_next()
#define _pack_next_intr(source, data) \
            printf("Pack \"%s\"... Size: %d, current offset: %d, current size: %d\n", #source, __size, __addr, sizeof(source)); \
            if ((__addr + (int)sizeof(source)) <= (__size)) \
            { \
                memcpy(__ptr + __addr, &(data), sizeof(source)); \
                __addr += sizeof(source); \
            } (void)(0)
 
/**
 Пакует очередной элемент
 */
#define pack_next(source) \
            _pack_next_intr(source, source)
 
/**
 Пакует очередной элемент, преобразовывая его в сетевой вид
 */
#define pack_net_next(source) \
            switch(sizeof(source)) \
            { \
                case 2: \
                    memcpy(&__swapd.ii[0], &source, 2); \
                    __swapd.ii[0] = htons(__swapd.ii[0]); \
                    _pack_next_intr(source, __swapd.ii[0]); \
                    break; \
                case 4: \
                    memcpy(&__swapd.ll[0], &source, 4); \
                    __swapd.ll[0] = htonl(__swapd.ll[0]); \
                    _pack_next_intr(source, __swapd.ll[0]); \
                    break; \
                case 8: \
                    memcpy(&__swapd.dd, &source, 8); \
                    __swapd.ll[0] = htonl(__swapd.ll[0]); \
                    __swapd.ll[1] = htonl(__swapd.ll[1]); \
                    _pack_next_intr(source, __swapd.dd); \
                    break; \
                default: \
                    pack_next(source); \
                    break; \
            } (void)(0)
 
/**
 Конец блока паковки данных
 */
#define pack_end()            unpack_end()

Можно ещё оптимизировать, тут оверхед с переменными вспомогательными, можно без них в большинстве случаев. Ну и, кажется, не совсем корректная работа с величинами больше 4 байт (double), но на серверной машине, по-моему, вообще залепа сделана, посмотрим как работать будет :)

Пример использования для отсылки данных:

struct Data {
  short     id;
  float      d;
  long      tm;
} data;
 
data.id = 0;
data.d  = 73.3;
data.tm = 0xFEFD;
 
int n;
 
size_t data_size = 2 + 4 + 4; // sizeof(short) + sizeof(float) + sizeof(long) - поля структуры
char *data_ptr;
 
data_ptr = calloc(data_size, sizeof(char));
 
pack_begin(data_ptr, data_size);
  pack_net_next(data.id);
  pack_net_next(data.d);
  pack_net_next(data.tm);
pack_end();
 
n = send(sock, data_ptr, data_size, 0);

Пример получения данных:

struct Data {
  short     id;
  float      d;
  long      tm;
} data;
 
int n;
 
size_t data_size = 2 + 4 + 4; // sizeof(short) + sizeof(float) + sizeof(long) - сколько данных получить нужно
char *data_ptr;
 
data_ptr = calloc(data_size, sizeof(char));
 
n = recv(sock, data_ptr, data_size, 0);
 
unpack_begin(data_ptr, data_size);
  unpack_net_next(data.id);
  unpack_net_next(data.d);
  unpack_net_next(data.tm);
unpack_end();
 
// Тут структура заполнена распакованными данными

Если преобразование в сетевой вид не нужно, используйте pack/unpack_next()

// Гоняем структуры по сети

Вспоминаем о выравнивании (не говорю о том, что числовые поля нужно привести в сетевой вид, дабы на удаленной стороне была уже произведена корректная распаковка данных: man 3 htons/ntohs, man 3 htonl/htohl, man 3 endian), чешем репу - а если приёмная сторона использует другое выравнивание, нежели у нас при сборке бинарника?2)

Вижу варианты:

  • пихать все в char-буффер - некрасиво
  • поэлементно отправлять - неудобно и некрасиво
  • использовать #pragma pack() - удобно, красиво, но не супер переносимо
  • использовать систему атрибутов GCC - не переносимо

Посмотрим на использование #pragma pack(), конкретно посмотреть подробности на официальной странице: http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html

При объявлении структуры, которая будет гоняться по сети рекомендуется отключить выравнивание:

#pragma pack(push,1)
....
#pragma pack(pop)

Простой пример:

#include <stdio.h>
 
#pragma pack(push,1)
struct test_packed {
  char ch;
  long data;
};
#pragma pack(pop)
 
struct test_unpacked {
  char ch;
  long data;
};
 
int main()
{
  printf("Packed size: %d, Unpacked size: %d\n",
         sizeof(struct test_packed),
         sizeof(struct test_unpacked));
  return 0;
}

Компилируем и выполняем:

$ gcc -o main main.c && ./main
Packed size: 5, Unpacked size: 8

Всё наглядно :)

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

UPD: тут #864110 подкинули ещё ссылку: http://www.leonerd.org.uk/code/libpack/

PS по слухам появилось в 2.95.2 или около того.

2)
по рабоче-крестьянски: на типичной 32бит машине все переменные выравнены по 32битной длинне