Дружественные шаблонные операторы

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

Buffer<int> buff;
int data1 = 100;
int data2 = 200;

buff << data1 << data2;


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

template<typename T>
class Buffer
{
public:
friend Buffer<T>& operator<< ( Buffer<T> buff, const T& data );

Buffer()
{}
~Buffer()
{}
};

template<typename T>
Buffer<T>& operator<<( Buffer<T>& buff, const T& data )
{
// do smth
return buff;
}

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

template<typename U>
friend Buffer& operator<< ( Buffer& buff, const U& data );

Но это не то что требуется. Хотя на самом деле, все просто и логично:

template<typename T>
class Buffer;

template<typename T>
Buffer<T>& operator<<( Buffer<T>& buff, const T& data );

template<typename T>
class Buffer
{
public:
friend Buffer<T>& operator<< <T> ( Buffer<T>& buff, const T& data );

Buffer()
{}
~Buffer()
{}
};

template<typename T>
Buffer<T>& operator<<( Buffer<T>& buff, const T& data )
{
// do smth
return buff;
}

Автоопределение прокси

Казалось бы, тривиальнейшая задача – определить настройки прокси сервиса, заданные при помощи .pac файла либо полученные в результате работы автоконфигурации. А в действительности оказывается что информации в интернете кот наплакал, а та что есть посвящена венде (что еще печальнее). Чтобы исправить эту жутко не справедливую ситуацию, я опишу как можно определить настройки прокси в Mac OS X приложении.
Итак, необходимый API появился в Mac OS X начиная с версии 10.5, так же API присутствует в iOS, но деталей по версиям я уже сказать не могу. Continue reading

printf и CoreFoudation

На мой взгляд, CoreFoudation очень интересная библиотека. Я бы сказал это лучший вариант реализации объектно ориентированной модели при разработке на Си, который я когда-либо видел. Но иногда возникают досадные мелочи, например невозможность использовать printf для стандартных объектов из CoreFoudation, а как было бы здорово.
Но, все не так уж и плохо, хотя и в гугле ответ находится не сразу:

#include <CoreFoundation/CoreFoundation.h>

int main (int argc, const char * argv[])
{
    CFStringRef outStr = CFStringCreateWithFormat(NULL, NULL, CFSTR("Output string: %@, %d"), CFSTR("additional string"), 10);
    CFShow( outStr );
    CFRelease( outStr );
    return 0;
}

Можно это дело еще в макрос завернуть, наверное, но зачем? Вобщем, пусть так будет.

C++ не нужен?

В преддверии лекции Страуструпа, посвященной C++0x, наша маленькая но разношерстная команда разработки под Mac OS X разошлась во мнениях о ее потенциальной полезности. Одна часть, что показательно молодежь, утверждает что и от книги мозги вскипают, а от лекции и подавно поплохеет. Другая часть, постарше, ожидает что будет очень интересно.
Так вот, получается довольно интересная ситуация. Приток новых людей в область сильно сокращется, новички хотят иметь в языке как минимум сборку мусора, а старые разработчики уходят кто куда.
С учетом всего этого, очень интересно, что ждет область лет через 10. С одной стороны, плюсы это далеко не настольно незаменимы как чистый Си, с другой стороны, на данный момент это единственный язык позволяющий писать достаточно шустрый код для конечного пользователя. Но, как ни крути, такая ситуация будет не всегда, мощность компьютеров рано или поздно позволит работать программам на управляемых языках с приемлимой скоростью.
В целом, тенденция с сокращением количества C++ разработчиков не так уж и плоха, будем как кобол-динозавры, жутко редкими и жутко дорогими. С другой, скучно, наверное, в говнищах 30 летней давности копаться? Но, на данный момент, описанная выше тенденция сплошной плюс – зарплаты растут, количество вакансий растет, интресных задач все больше.

Перехват вызовов DTrace

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

Изучение DTrace стоит начать с его “эпицентра” (как утверждают авторы фрэймворка) – функции dtrace_probe. В целом, код достаточно понятен и содержит в себе большое количество комментариев. После внимательного изучения этой функции становится ясно, что искомое находится чуть дальше, в функции fbt_provide_module, которую можно найти в файле fbt.c директории bsd/dev/dtrace исходных кодов XNU. Эта функция как раз и используется для патчинга перехватываемых функций.
Итак, что же он делает в случае с 64 битной архитектурой (для 32 битной или PPC используется тот-же принцип)? Сам процесс установки перехватчика выглядит следующим образом: у каждого модуля берется таблица символов (struct nlist_64) и каждый глобальный символ, имеющий имя, сверяется со списком требуемых “проб”. При этом DTrace отказывается перехватывать ряд системных функций, например, такие как собственные функции (dtrace_*), функции, отвечающие за отладку (kdp_*, kdbg_*, kernel_debug), работу таймеров (etimer_*, clock_*, absolutetime_to_*), управление питанием (acpi_*), и ряд других. Если найденную функцию необходимо перехватить, то делается проверка того, что функция начинается со следующей последовательности команд (адрес начала функции получается из той же таблицы символов):

push %rbp                       ; 0x55
mov %rsp, %rbp                  ; 0x48, 0x89, 0xE5

Затем ищется конец функции. Если все прошло успешно, то создается “проба”, в которой сохраняется внутренняя информация, включая информацию о найденных адресах. На этом создание пробы завершается и начинается обработка следующей функции из списка.

Ну, а теперь самое интересное. После того как проба становится активной (функция fbt_enable), второй байт от начала перехватываемой функции подменяется на 0xF0. Копирование происходит по физическим адресам в памяти при помощи функции ml_nofault_copy. В результате возникает гарантировано неверная команда и, как следствие, возникновение исключения при попытке ее обработки процессором. Ну, а дальше все довольно очевидно. Возникающее исключение перехватывается DTrace, и идет обработка сработавшей “пробы” с последующим вызовом оригинальной функции.

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

Грабля в launchd

Неожиданно нарвался на граблю там, где уж совсем не ожидал ее найти, а именно в launchd. Вообще, launchd крайне удобен в управлении демонами, например при помощи него мы при запуске GUI из под пользователя поднимаем рутовый демон. Делается это довольно просто, создается plist и в переменную WatchPath указывается директория, по изменении которой должно запуститься приложение из ProgrammArgument. Сам plist помещается в директорию LaunchAgents для пользовательских приложений и LauchDaemons для системных.
Описанная выше часть работает как часы, и все бы ничего, если бы не возникла необходимость запустить по событию не только демон, но и само GUI. Казалось-бы все просто – добавляем WatchPath со ссылкой на ту же директорию, что и в случае с нашим демоном и вперед. Но вот тут то и зарыта грабля – демон стартует великолепно, а GUI нет. Чисто теоретически, такого происходить не должно, особенно с учетом того, что за директорией LaunchAgents следит launchd запущенный из-под пользователя, а за LauchDaemons рутовый. Но, это только теория, на практике все несколько иначе.
Лечиться созданием отдельной директории, используемой для запуска GUI приложения, которое уже в свою очередь поднимает демон.