пятница, 20 апреля 2018 г.

Поиск функцией std::find_if по ассоциативному спику std::map / Searching by std::find_if on std::map

Иногда надо перебрать все элементы ассоциативного списка и можно это сделать традиционно, с помощью цикла for.
Но есть более изящное решение с использованием возможностей C++11. Допустим у нас есть ассоциативный список типа  std::unordered_map<int, MyType>.
Вот как это можно сделать:

// very clear lambda statement
auto pred = [sVal](const std::pair<int, MyType>& x) { return x.second.val == sVal; }; 
const auto it = std::find_if(myMap.begin(), myMap.end(), pred);

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

// does the same, but looks much smarter ^_^
auto pred = [sVal](const decltype(myMap.begin())::value_type& x) { return x.second.val == sVal; }; 
const auto it = std::find_if(myMap.begin(), myMap.end(), pred);

Получилось боле громоздко, но зато более универсально.

четверг, 5 апреля 2018 г.

Определить разрядность процессора во время компиляции / Detect CPU bit capacity during compiling

Если, например, у нас есть две версии функции: одна для 32-разрадных процессоров и вторая для 64-разрядных процессоров. И мы хотим использовать ту, которая подходит для нашей архитектуры процессора.
Первый путь это использовать директивы препроцессора (дефайны):

void do_some()
{
#if defined(_IS_64_BIT_CPU)
    do_some_64();
#endif

#if defined(_IS_32_BIT_CPU)
    do_some_32();
#endif
}

Иногда, когда мы не имеем таких директив, можно определить разрядность процесора по размеру указателя (у 64-битных процессоров он очевидно равен 8 байтам, и соотсвественно у 32-разрядных процессоров он равен 4 байтам):

void do_some()
{
if(sizeof(void*) == 8)
    do_some_64();

if(sizeof(void*) == 4)
    do_some_32();
}

Это работающее решение, но нам не нужна одна из веток, т.е. мы хотим ее исключить на этапе компиляции. Можно с помощью C++11 написать такой код:

template<typename T = void*>
std::enable_if_t<sizeof(T) == 8, void> do_some()
{
    do_some_64();
}

template<typename T = void*>
std::enable_if_t<sizeof(T) == 4, void> do_some()
{
    do_some_32();
}

Теперь на этапе компиляции будет включено только одно из определений функции - то, которое нам нужно.
Хотя, проще для чтения все же вариант с дефайнами, но второй вариант более универсален.

пятница, 15 сентября 2017 г.

Определить архитектуру CPU в Qt .pro-файле / detect a CPU architecture in Qt .pro-file

Очень часто надо определить архитектуру в .pro-файле Qt-проекта, чтобы прилинковать нужную версию библиотеки. У меня это обычно выбор между архитектурами x86 или x86_64. Нагуглил вот такое работающее решение:

    contains(QT_ARCH, x86_64) {
        LIBS += ../../linux.x86_64/mymodule.a
    } else {
        LIBS += ../../linux.x86/mymodule.a
    }
Очень удобно, и теперь не надо руками возиться с зависимостями от компилятора или создавать какие-то свои скрипты.

среда, 13 сентября 2017 г.

Добавить строковый дефайн в Qt pro-файле / How to add string define in qt pro-file

Добавление числовых определений для препроцессора или строк без пробело и кавычек достаточно тривиально:

DEFINES += "MYDEF=SOME_STR_1234"

А если строка с пробелами? У меня компилятору понравилось вот такое выражение:

DEFINES += "MYDEF=\"SOME  STR  123 \` *\""

То есть можно использовать бэкслеш для  экранирования спецсимволов и кавычек в том числе.

Теперь можно использовать этот дефайн как строку:

#define xstr(a) str(a)
#define str(a) #a
printf("string [%s]", xstr(MYDEF));

вторник, 5 сентября 2017 г.

How to add file as binary to resources in C# assembly / Как добавить бинарный файл в ресурсы

Достаточно банальная задача, но в Visual Studio 2015 она не решается там просто. Я не нашел никаких способов, чтобы сделать это "правильно". Вижуал Студио постоянно считал, что умнее меня и хранил файл как рисунок или файл соответствующего типа (Icon, Bitmap, Image или String). Единственное, что помогло - это руками подправить resx-файл:

<data name="MyBinaryFile" type="System.Resources.ResXFileRef, System.Windows.Forms">
    <value>..\Resources\MyBinaryFileName.gif;System.Byte[], mscorlib, Version=4.0.0.0,       Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>

Весь фокус в том, чтобы выставить System.Byte[] в качестве типа импортируемого ресурса. Теперь жмем "rebuild project". Теперь этот файл можно получать вот так:

Byte[] bytes = (Byte[]) global::MyAssembly.Properties.Resources.ResourceManager.GetObject("MyBinaryFile");

Или так:

Byte[] bytes = global::MyAssembly.Properties.Resources.MyBinaryFile;




вторник, 1 августа 2017 г.

bash-скрипт для получения информации о зависимостях

При развертывании приложений в различном окружении часто бывает, что непонятно, какие библиотеки нужно добавлять в дистрибутив, а какие уже и так установлены в ОС. Чтобы это автоматически прояснить, я создал простой скрипт, который просто проверяет, все ли библиотеки, от которых зависит данная библиотека, доступны:

#!/bin/bash

# set env.
export LD_LIBRARY_PATH=$PWD

echo " --- Environment variables:"
echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH"

echo " --- Check library dependencies:"
SO_FILES=lib*.so*
LIBS_CNT=0
LIBS_WITH_NOT_FOUND_DEPS_CNT=0

for f in $SO_FILES
do
    LIBS_CNT=$((LIBS_CNT+1))
    LDD_RES=$(ldd $f)
    if [[ $LDD_RES == *"not found"* ]];
    then
        echo "ldd $f [NOT FOUND ONE OR MORE DEPENDENCIES]"
        ldd $f
        LIBS_WITH_NOT_FOUND_DEPS_CNT=$((LIBS_WITH_NOT_FOUND_DEPS_CNT+1))
    else
        echo "ldd $f [OK]"
    fi
done

echo " --- Results:"
echo "LIBS_CNT $LIBS_CNT"
echo "LIBS_WITH_NOT_FOUND_DEPS_CNT $LIBS_WITH_NOT_FOUND_DEPS_CNT"

В результате удачного запуска (все зависимости найдены) будет такое:

[vasya@localhost the_app]$ ./check_so_deps.sh
 --- Environment variables:
LD_LIBRARY_PATH=/home/vasya/the_app
 --- Check library dependencies:
ldd libc-2.12.so [OK]
ldd libc.so [OK]
ldd libc.so.6 [OK]
ldd libexif.so.12 [OK]
ldd libexif.so.12.3.3 [OK]
ldd libgdiplus.dll.so [OK]
ldd libgdiplus.so.0 [OK]
ldd libgdiplus.so.0.0.0 [OK]
ldd libgif.so.4 [OK]
ldd libgif.so.4.1.6 [OK]
ldd libQtCore.so [OK]
ldd libQtCore.so.4 [OK]
ldd libQtCore.so.4.6 [OK]
ldd libQtCore.so.4.6.2 [OK]
ldd libQtGui.so [OK]
ldd libQtGui.so.4 [OK]
ldd libQtGui.so.4.6 [OK]
ldd libQtGui.so.4.6.2 [OK]
ldd libungif.so.4 [OK]
ldd libungif.so.4.1.6 [OK]
ldd libX11.so [OK]
ldd libX11.so.6 [OK]
ldd libX11.so.6.3.0 [OK]
 --- Results:
LIBS_CNT 29
LIBS_WITH_NOT_FOUND_DEPS_CNT 0

понедельник, 31 июля 2017 г.

Linux mono: DllNotFoundException & Dll mapping

Недавно возникла необходимость создать standalone-пекедж с приложением для Linux на mono. Традиционно эта задача решается очень просто: в папку с приложением накидываются все необходимые файлы, пути, где лежат библиотеки, добавляются в LD_LIBRARY_PATH (для обычных *.so-библиотек) и в MONO_PATH (для библиотек mono). Скрипт запуска будет выглядеть так:

export LD_LIBRARY_PATH=$PWD
export MONO_PATH=$PWD
./mono myprog.exe

И вроде бы все прекрасно, но постоянно валятся ошибки типа DllNotFoundException:

System.TypeInitializationException: The type initializer for 'System.Windows.Forms.XplatUI' threw an exception. ---> System.TypeInitializationException: The type initializer for 'System.Drawing.GDIPlus' threw an exception. ---> System.DllNotFoundException: gdiplus.dll

Причем есть два способа решения этой задачи. Первый способ это классический linux-way: просто создаем символическую ссылку с таким названием на нужную библиотеку (в моем случае это libgdiplus.so.0.0.0, например). Но это не очень хороший способ, т.к. нужно угадать с названием, которое может быть и gdiplus.dll.so и libgdiplus.so и еще какое-то. Лучше всего это узнать, включив дебаг:

MONO_LOG_LEVEL=debug  ./mono myprog.exe

и посмотрев, какие библиотеки вызывает mono-фрейворк.

Второй способ, на мой взгляд, лучше: использовать Dll-mapping. Для этого создадим файл System.Drawing.dll.config (поскольку именно библиотека System.Drawing.dll сгенерировала это исключение) следующего содержания (естественно, у вас имя нативного файла может отличаться):
<configuration>
  <dllmap dll="gdiplus.dll" target="libgdiplus.so.0.0.0"/>
</configuration>
Естественно, что для этого надо знать, в какой библиотеке (сборке) находится вызов, который требует ту или иную нативную библиотеку, иначе маппинг не будет работать. В чем преимущество такого способа? Ну например в том, что не надо гадать, какое имя ищет библиотека, когда говорит что не может найти какую-то *.dll (не говоря уже о том, что нативные *.dll-библиотеки под linux уже как-то запутывают, когда знаешь, что они должны быть с расширением *.so). Т.е. мы прямо указываем фреймворку, где искать нативную библиотеку и не пытаемся "угадывать" правильное название библиотеки.