25 февр. 2009 г.

Инструмент "В контакте"

Начал новый проект: https://launchpad.net/vkontakte-tool

Для ленивых ходить по ссылкам, это - аналог программы Vkonpic, но работающий в Linux. Позволяет загружать на стены в контакте фотки вместо графити.
Написан на Python, то есть является кроссплатформенным.

На данный момент доступны исходный код и deb-пакет для Ubuntu.
Попозже будет доступна сборка для Windows.

11 февр. 2009 г.

Использование ссылок при обработке исключений C++

Просто предупреждаю.

Не попадитесь:
class AException {
public:
const int& x;
AException(const int& _x)
: x(_x)
{
}
}

class A {
private:
int* x;

public:
A()
{
x = new int;
*x = 2;
}
~A()
{
*x = 1;
delete x;
x = NULL;
}

void throw_func()
{
throw AException(*x);
}
};

void a_runner()
{
A a;
a.throw_func();
}

int main()
{
try
{
a_runner();
}
catch(AException& e)
{
cout << "Cought exception with x = " << e.x << endl;
}
}
Мораль сей басни такова. Когда вы в обработчике исключения попытаетесь обратиться к e.x, он уже будет некорректный, так как экземпляр объекта A будет уничтожен (вызовется деструктор) ДО того, как управление передается обработчику исключения.

6 февр. 2009 г.

Нюансы множественного наследования в C++

Недавно открыл для себя виртуальное наследование в C++.

Такой способ наследования тесно связан с другой возможностью C++ - множественным наследованием. Я бы даже сказал, он вполне логично дополняет один из способов применения множественного наследования.

Рассмотрим такой пример:

class A {
public:
int var_a;
virtual void foo() = 0;
};

class B: public A {
public:
int var_b;
void bar()
{
foo();
}
};

class C: public A {
public:
int var_c;
void foo()
{
cout << "This is C::foo()" << endl;
}
};

class D: public B, public C {
int var_d;
};
Все дальнейшие манипуляции с классами будем проводить в main().
Например, объявим экземпляр класса D и попробуем присвоить что-нибудь переменной var_a:
D d;
d.var_a = 2;
Компилятор не пропустит такое выражение и вот почему. Дело в том, что при использовании обычного невиртуального наследования экземпляр объекта D имеет следующую логическую структуру:

Как видно из схемы, мы имеем две переменных var_a, одна их которых принадлежит классу A, который является базовым классом для B, а другая принадлежит классу A, который является базовым классом для C.
Для обхода этой ситуации можно конечно явно указать, какую переменную мы имеем ввиду:
d.B::A::var_a = 2;
или:
d.C::A::var_a = 2;
Но это, согласитесь, не очень красиво, да и не всегда логически необходимо иметь два экземпляра базового класса.
Вот именно в этом случае к нам приходит на помощь виртуальное наследование.
Есть и еще один нюанс в нашем примере: взгляните на функцию B::bar(). В ней происходит вызов виртуальной функции A::foo(), которая для этой ветви иерархии неопределена - ни в классе A, ни в классе B, ни в классе D. К классу C функция B::bar() доступа не имеет, так как C по сути наследует от другого экземпляра A.

Что же будет, если применить виртуальное наследование? Слегка изменим наш пример:
class A {
public:
int var_a;

virtual void foo() = 0;
};


class B: public virtual A {
public:
int var_b;

void bar()
{
foo();
}
};


class C: public virtual A {
public:
int var_c;
void foo()

{
cout << "This is C::foo()" << endl;
}
};

class D: public B, public C {
int var_d;
};

Теперь экземпляр класса D будет иметь другую логическую структуру:

Как видно из рисунка теперь для классов B и C создается единственный общий экземпляр класса A.
Таким образом код
D d;
d.var_a = 2;

становится допустимым.

При применении виртуального наследования становится возможном и еще один интересный финт ушами - использование виртуальной функции, фактически определенной не в родительском и не в дочернем классах, а в классе-брате. В нашем примере - это виртуальная функция A::foo(), которая определена в классе C - C::foo(). Посмотрите, она используется из функции B::bar(), который является братом класса C.
Естественно, чтобы это работало, мы должны создать экземпляр такого объекта, который бы наследовал от обоих классов B и C, в нашем случае - это класс D. Таким образом, становится допустимым код:
D d;
B& b = d; //создали ссылку типа B на объект типа D.
b.bar(); //вызываем функцию bar(), которая в свою очередь вызывает функцию foo(), которая фактически определена в классе C.

Программиста, который получил в свой метод ссылку типа B, вообще может ввести в заблуждение работоспособность подобного кода, ведь он не знает о том, что на самом деле эта ссылка указывает на объект типа D (он вовсе не видит объявления класса D), который наследует и от C тоже. С его точки зрения это будет казаться мистикой - ведь функция bar() вызывает виртуальную функцию foo(), которая с его точки зрения НИГДЕ не определена!

Ну и напоследок еще одна задачка, связанная с множественным наследованием:

class B
{
public:
int var_b;
};

class C
{
public:
int var_c;
};

class D: public B, public C
{
public:
int var_d;
};

using namespace std;

int main()
{
D* d = new D;

B* b = d;
C* c = d;

void* v_d = d;
void* v_b = b;
void* v_c = c;

cout << (b == d) << endl;
cout << (c == d) << endl;

cout << endl;

cout << (v_b == v_d) << endl;
cout << (v_c == v_d) << endl;

return 0;
}
Программа выведет:
1
1
1
0

Внимание вопрос: почему в последнем случае получается ноль (false)? :)

29 дек. 2008 г.

ALSA и FFmpeg

На днях задался целью дописать поддержку захвата звука к одному свободному приложению (если быть точным, то это Motion).

С инструментарием определился быстро:
Для захвата звука - ALSA, так как моя звуковая карта работает именно с ней.
Для сжатия и сохранения в файл - FFmpeg (его библиотеки avcodec и avformat), так как именно ими пользуется Motion для сохранения видео.

С ALSA все более-менее понятно, есть немного внятная документация и автоматически сгенерированное описание API там же. Также я смотрел исходники программы aplay и arecord (которые на самом деле - одна и та же программа).
А вот с FFmpeg всё оказалось сложнее - никакой внятной документации для него нет, даже на английском языке. Есть автоматически сгенерированное описание API и несколько примеров:
  1. В самом проекте - показано, как использовать avcodec на примере кодирования/декодирования аудио и видео отдельно друг от друга. При этом работа с медиафайлами реализована напрямую без использования avformat.
  2. Пример от Martin Böhme рассказывает о том, как с помощью avformat и avcodec открыть и декодировать видеофайл. Он не касается звука, а также кодирования аудио/видео информации.
  3. Пример от dranger являет собой урок по созданию с нуля простенького видеоплеера, который для чтения и декодирования файлов использует avformat и avcodec, а для воспроизведения видео и звука, а также для взаимодействия с клавиатурой использует библиотеку SDL. Здесь тоже не затрагиваются вопросы кодирования.
Поскольку я до этого никогда не программировал с использованием ALSA и FFmpeg, то, прежде чем что-то внедрять в Motion, я решил написать простенькую програмку для захвата звука со звуковой карты и сжатия его в формат ogg.

Вот что у меня получилось: http://ls-it.googlecode.com/svn/trunk/alsa-capture/
Для сборки просто введите make. После запуска программа попытается записать несколько секунд звука с ALSA-устройства default в файл output.ogg, который будет создан в папке с программой.

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

24 дек. 2008 г.

Открытие

Всем, как говорится, доброго здравия!
В последнее время накопилось много всего, чем хочется или просто необходимо поделиться с миром.

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

В блоге "L-S: IT" я планирую размещать свои мнения и предложения относительно различных проявлений такой области как Информационные Технологии. :)