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

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



// C++: операторы сравнения для структур

Памятка:

Т.е. используя std::tie() вполне, без труда и пыли, можно сделать операторы сравнения для сложных структур. Сложности могут возникнуть, если нужно переопределить операторы сравнения для отдельных элементов (например: критерии сравнения строк).

Почему tie, а не напрямую tuple? tie захватывает аргументы по ссылке - экономия на копировании.

// std::string_view и временные объекты

Идея поста родилась при употреблении чая во внутрь на южной кухне.

Недавно смотрел один доклад (точнее бегло просматривал) про Rust и в момент, когда начался рассказ про life-time, глаз зацепился за такой опасный пример из мира C++:

string get_url()
{
  return "http://htrd.su";
}
 
string_view get_schema(string_view v)
{
  // тут какие-то действия, я их опущу
  auto result = v;
  return result;
}
 
int main()
{
  auto v = get_schema(get_url());
}

Что такое string_view - смотреть тут или тут. Если коротко - это невладеющая строка. Полезна для экономии на аллокациях, когда нужно работать с частями исходной строки.

В общем, из природы string_view следует и проблемы в коде выше: get_url() вернёт временный объект, который будет уничтожен в конце выражения, а следовательно, v будет ссылаться на невалидный участок памяти.

У меня в голове родилось, сходу, вариант защиты от такого: так как string_view не владеет строкой, то перемещение для строки сделать невозможно (да и семантически неверно), а перемещающий конструктор будет предпочтён для временного объекта. Следовательно если сделать перемещающий конструктор для string у string_view удалённым, то код выше сломается на этапе компиляции.

// C++11: несуразность std::thread

Пост-вопрос.

Может кто объяснить, почему в стандарт вошла настолько обрезанная версия реализации std::thread? Ведь предлагаемый интерфейс не предоставляет абсолютно никаких средств передачи параметров потоку в момент создания, к примеру, тот же размер стека, что крайне актуально на всяких RTOS. При этом Boost.Thread такую возможность предоставляют средствами boost::thread::attributes:

    template <class F>
    explicit thread(attributes& attrs, F f);
 
    template <class F>
    thread(attributes& attrs, F &&f);
 
    template <class F, class ...Args>
    explicit thread(attributes& attrs, F&& f, Args&&... args);

Средства хоть и лимитированные, но доступ к native_handle атрибута позволяют тюнить параметры на конкретной платформе. Но в стандартной библиотеке нет и их. Нипанимать. Explain. Explain.

PS libstdc++ зато предоставляет достаточно простые средства, что бы добавить поддержку своих потоков (на той же RTOS), не прибегая к модификации кода библиотеки (если кому интересно, могу на пальцах разбросать как, но без полной реализации).

// C++11: std::error_code & std::error_condition в GCC 4.8/4.9 и 5.1

Кусок кода:

    auto ecod = make_error_code(std::errc::resource_unavailable_try_again);
    auto econ = make_error_condition(std::errc::resource_unavailable_try_again);
    assert(ecod == econ);

Согласно документации ассерт не должен срабатывать. Компилятор GCC 4.8 и 4.9 в Linux Mint собирает код и выдаёт такое при запуске:

$ ./webserver
webserver: webserver.cpp:661: int main(int, char**): Assertion `ecod == econ' failed.
Aborted

Внезапно.

GCC 5.1 собирает и нормально обрабатывает ситуацию.

// AvCpp: API-2 rework, веха вторая

Продолжаем отслеживать судьбу изменений, описанных в AvCpp: API-2 rework, веха первая.

Как обычно, объём работ отличается от того, что было запланировано. Итак, что сделано:

  1. Работа со словарями и поддержка оных во всех местах, где только можно. Это вызвало необходимость разнести открытие входного потока и поиск информации о стриме (Снова FFmpeg и low-latency). Как следствие, добавился новый вызов: av::FormatContext::findStreamInfo(). Он может принимать коллекцию словарей для каждого стрима в потоке.
  2. Начата работа над фильтрами. Уже появилась базовая инфраструктура. Но пока нет понимания в каком виде оно должно получиться на выходе.
  3. В рамках чистки кода тотально переработана обработка ошибок. Теперь это делается через std::error_code, который передаётся опциональным аргументом в те функции фейл которых может приводить к неоднозначностям в будущем. Если переменная для кода ошибки не передана, будет выброшено исключение av::Exception которое будет содержать данный код. Планируется, что набор исключений расширится под каждую сущность. Данные изменений повлекли достаточно сильные изменений в API, требующие изменений в коде приложений, которые их используют. При обработке ошибок стоит учитывать категорию оных. На дынный момент используется, как минимум три:
    • avcpp_category() - для внутренних ошибок AvCpp
    • ffmpeg_category() - для ошибок, пришедших от FFmpeg
    • system_category() - для системных ошибок, в основном так рапортуются ошибки выделения памяти при помощи встроенных av_alloc() (когда владение принимается FFmpeg'ом и он сам освобождает, как следствие, использовать new/new[] нет возможности). Целесообразность использовать std::bad_alloc обдумывается.

Ну и главное, реорганизация бранчей:

  • все наработки первой вехи выделены в отдельный бранч api-2.0 (исправляются только ошибки)
  • старый мастер стал бранчем api-legacy (для совместимости, самостоятельно тут ничего не исправляется, только принимаются мерж-реквесты)
  • вся разработка перемещена на бранч master

Что осталось:

  1. Интерфейс опций
  2. Доделать фильтры
  3. Ревизия и чистка кода в т.ч. примеров.

Планируется, что это будет сделано в следующей, третье вехе.

Как обычно, за отзывы, багрепорты и пул-реквесты буду благодарен.

// AvCpp: API-2 rework, веха первая

Для начала, что такое AvCpp - это C++ враппер над FFmpeg, позволяющий несколько упростить работу непосредственно с функциями FFmpeg.

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

Изначально структура классов опиралась на Xuggle API, ведь когда начинал его делать, многое не понимал - почему оно так. Со временем такой подход перестал мне нравится, начали вылазить всякие неприятные оверхеды, баги. Так появился подпроект API-2, цели которого:

  1. Уйти где только можно от shared_ptr (это получилось почти везде).
  2. Использовать по максимуму возможности новых версий FFmpeg, где появился reference-counting для AVFrame/AVPacket, при этом предоставить возможности создавать «тяжёлые» копии, где нужно.
  3. Более логично организовать мапинг сущностей FFmpeg в AvCpp, к примеру, если раньше AVCodecContext мапился в av::StreamCoder, то теперь av::CodecContext, аналогично для AVFormatContext (av::Container vs av::FormatContext).
  4. Корректно реализовать аудио-ресемплер
  5. Добавить побольше примеров использования API2
  6. Работа с av_options для поддерживаемых сущностей
  7. Поддержка передачи параметров через словари
  8. Тотальная переработка фильтров
  9. Чистка кода, удаление ненужных сущностей

На текущий момент на бранче api-2 уже лежат почти все эти наработки. Нереализованными остались последние 4 пункта. Приэтому, попутно, устранено несколько багов, особенно связанных с последовательностью уничтожения разных объектов (например Stream2 и FormatContext: стримом владеет FormatContext, Stream2 только предоставляет доступ к нему, при этом, даже если уничтожить объект контекста, объект Stream2 может теперь корректно рапортовать, что он инвалидировался).

Вторая веха, судя по всему, будет реализация поддержки AVOptions/AVDict, третья веха - фильтры (к слову их сейчас нет вообще в рамках API-2). Последующие вехи пока не сформированы, скорее всего нужно будет подумать над возможностями использования аппаратного ускорения для декодирования (оно достаточно некрасиво и в самом FFmpeg реализовано - нужно делать много телодвижений).

В любом случае, я уже настоятельно рекомендую начинать использовать ветку api-2. В скором времени она заместит master-ветку, а текущий мастер переедет на ветку legacy.

За отзывы, багрепорты и пул-реквесты буду благодарен.

// C++11: паттерн Transaction

Переосмысление реализации паттерна Transaction, опубликованного в "Inside C++", в рамках стандарта C++11 с блек… variadict templates и хранением данных на стеке.

// Пополняем шпаргалки по C++: неявно-генерируемые перемещающий конструктор и оператор присваивания

Статью изначально публиковал на хабре: http://habrahabr.ru/post/232775/. Здесь - для единства мыслей :)

Когда не так часто, как хотелось бы, приходится работать с языком, некоторые аспекты забываются. А некоторые никогда и не откладываются в голове. Поэтому, когда возникают вопросы, приходится отвлекаться и лезть в документацию.

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

Под катом я решил опубликовать пару шпаргалок для определения условия когда будет создан компилятором неявно-генерируемый перемещающий конструктор и перемещающий оператор присваивания.

Шпаргалки представлены в виде PDF файлов для печати на принтере A4, в виде картинки PNG, а также исходников в SVG.

// erase_if

Условное удаление по значению из всяких там map и иже с ними. А так же, бонусом, наиболее оптимальный вариант условного удаления для вектора.

// std::begin() и std::end()

С приходом нового стандарта эти две свободные функции появились в STL, в библиотеке итераторов (#include <iterator>). Кроме того, в примерах кода часто можно увидеть, что они используются на STL контейнерах вместо собственных методов .begin() и .end():

#include <iostream>
#include <vector>
#include <iterators>
 
using namespace std;
 
int main()
{
  vector<int> v{1, 2, 3, 4, 5};
 
  for (auto it = begin(v); it != end(v); ++it)
  {
    cout << "Number: " << *it << endl;
  }
 
  return 0;
}

Мне хорошо понятно, что эти функции отлично подходят для RAW-массивов, т.е. если в примере выше заменить:

vector<int> v{1, 2, 3, 4, 5};

на

int v[] = {1, 2, 3, 4, 5};

или даже

int v[]{1, 2, 3, 4, 5};

То весь остальной код менять не нужно. Удобно, причём мы защищены от подсовывания указателя вместо массива. Но почему они применяются для STL контейнеров?

Поиск по интернету дал только один ответ: для унификации. И только.

Т.е. ни по «кошерности» ни по скорости два этих подхода не отличаются. Чисто для единства вида.

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

Ссылки

// Qt Creator и C++11

Небольшая заметка о том, как форсировать поддержку C++11 в парсере для различных билд-систем. Заметки касаются master-снапшота QTC (брать тут: http://download.qt-project.org/snapshots/qtcreator/master/latest/ или собирать из исходников).

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

// C++11 regex

В синтаксисе регулярок в GCC 4.8.1/libstdc++4.8 нет поддержки квадратных скобок: []. Пришлось использовать Boost.Regex

Ах да, пруф: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53631

// Удаление weak_ptr из std::list

Дано: контейнер примерно такого вида:

std::list< boost::weak_ptr<Item> > items;

Вместо boost::weak_ptr<> могу быть:

  • std::tr1::weak_ptr<>
  • std::weak_ptr<> для C++11

Задача: нужно удалить элемент по значению.

Казалось бы, просто сделай:

...
items.remove(value);
...

ан нет: для weak_ptr<> не определён оператор сравнения. Если потеоретизировать, можно предположить, почему так сделано: что бы гарантировать консистентность указателей при сравнении нужно их захватить (сделать value.lock()), т.е. создать два shared_ptr и уже их сравнивать, т.е. лишние накладные расходы.

Поэтому удаление можно делать так:

template <typename T>
bool operator == (const boost::weak_ptr<T>& a, const boost::weak_ptr<T>& b)
{
    return a.lock() == b.lock();
}

после такого std::list::remove(const T&) будет работать для всех типов. Можно и сузить до конкретного.

Либо использовать std::list::remove_if(Predicate), предикат объявить как:

struct EqPredicate
{
    const boost::weak_ptr<Item>& item;
 
    EqPredicate(const boost::weak_ptr<Item>& item)
        : item(item)
    {
    }
 
    bool operator () (const boost::weak_ptr<Item>& p) const
    {
         return p.lock() == item.lock();
    }
};

и использовать так:

std::list< boost::weak_ptr<Item> > items;
...
items.remove_if(EqPredicate(value));

Информация взята отсюда: http://stackoverflow.com/questions/1390340/how-can-i-use-stdremove-on-a-container-with-stdtr1weak-ptr

Вышесказанное верно для boost::weak_ptr, std::tr1::weak_ptr, std::weak_ptr