[Перевод] 2.3 Работа с пользовательскими потоками данных

bc794ab377be444f8090426e56ae38bd.png

От переводчика: данная статья является восьмой в цикле переводов официального руководства по библиотеке SFML. Прошлую статью можно найти тут. Данный цикл статей ставит своей целью предоставить людям, не знающим язык оригинала, возможность ознакомится с этой библиотекой. SFML — это простая и кроссплатформенная мультимедиа библиотека. SFML обеспечивает простой интерфейс для разработки игр и прочих мультимедийных приложений. Оригинальную статью можно найти тут. Начнем.

Оглавление:
0.1 Вступление

1. Приступая к работе

  1. SFML и Visual Studio
  2. SFML и Code: Blocks (MinGW)
  3. SFML и Linux
  4. SFML и Xcode (Mac OS X)
  5. Компиляция SFML с помощью CMake

2. Модуль System
  1. Обработка времени
  2. Потоки
  3. Работа с пользовательскими потоками данных

3. Модуль Window
  1. Открытие и управление окнами
  2. Обработка событий
  3. Работа с клавиатурой, мышью и джойстиками
  4. Использование OpenGL

4. Модуль Graphics
  1. Рисование 2D объектов
  2. Спрайты и текстуры
  3. Текст и шрифты
  4. Формы
  5. Проектирование ваших собственных объектов с помощью массивов вершин
  6. Позиция, вращение, масштаб: преобразование объектов
  7. Добавление специальных эффектов с шейдерами
  8. Контроль 2D камеры и вида

5. Модуль Audio
  1. Проигрывание звуков и музыки
  2. Запись аудио
  3. Пользовательские потоки аудио
  4. Спатиализация: звуки в 3D

6. Модуль Network
  1. Коммуникация с использованием сокетов
  2. Использование и расширение пакетов
  3. Веб-запросы с помощью HTTP
  4. Передача файлов с помощью FTP


Вступление


В SFML есть классы ресурсов (изображений, шрифтов, звуков и т.д.). Во многих программах эти ресурсы будут загружаться из файлов с помощью функции loadFromFile. В некоторых ситуациях ресурсы будут непосредственно упакованы в исполняемый файл или в большой файл данных и будут загружаться из памяти с помощью функции loadFromMemory. Эти функции могут удовлетворить практически любые потребности, но не все.

Вы можете захотеть загрузить файлы из необычного места, такого как сжатый/зашифрованный архив, или, например, из удаленной сетевой папки. Для этих особых ситуаций, SFML предоставляет третью функцию: loadFromStream. Эта функция считывает данные, используя абстрактный интерфейс sf: InputStream, который позволяет вам иметь собственную реализацию класса потока, который будет работать с SFML.

В этой статье будет рассказано, как писать и использовать ваши собственные классы потоков.

Стандартные потоки C++?


Как и многие другие языки, C++ уже содержит класс потока данных: std::istream. По факту, таких классов два: std::istream является только front-end решением, абстрактным интерфейсом к данным, получаемым из std::streambuf.

К сожалению, эти классы не дружелюбны к пользователю — решение нетривиальной задачи с использованием данных классов может стать очень сложным. Библиотека Boost.Iostreams стремится обеспечить простой интерфейс для стандартных потоков, но Boost — это большая зависимость.

По этой причине SFML предоставляет собственный потоковый класс, который, как мы надеемся, более простой и быстрый, нежели перечисленные выше.

InputStream


Класс sf: InputStream декларирует четыре виртуальные функции:

class InputStream
{
public :

    virtual ~InputStream() {}

    virtual Int64 read(void* data, Int64 size) = 0;

    virtual Int64 seek(Int64 position) = 0;

    virtual Int64 tell() = 0;

    virtual Int64 getSize() = 0;
};


read должна извлекать size байт из потока и копировать их по предоставленному адресу. Возвращает число прочитанных байтов или -1 при возникновении ошибки.

seek должна изменять текущую позицию в потоке. Аргумент является абсолютным числом байтов для перемещения (отсчет идет от начала области данных, а не от текущей позиции). Возвращает новую позицию или -1 при возникновении ошибки.

tell должна возвращать текущую позицию (число байт от начала области данных) в потоке или -1 в случае возникновения ошибки.

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

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

FileInputStream и MemoryInputStream


В SFML 2.3 были добавлены два новых класса, предоставляющих потоки для управления новым модулем аудио. sf::FileInputStream предоставляет поток для чтения файла, sf::MemoryInputStream служит для чтения потока данных из памяти. Оба класса разработаны на основе sf: InputStream и могут использоваться полиморфно.

Использование InputStream


Использовать пользовательский потоковый класс просто: инстанцируйте его и передайте его в качестве аргумента функции loadFromStream (или openFromStream) объекта, который вы хотите инициализировать потоком.

sf::FileStream stream;
stream.open("image.png");

sf::Texture texture;
texture.loadFromStream(stream);


Примеры


Если вам нужна демонстрация работающего кода, и вы не хотите заблудиться в деталях реализации, вы можете взглянуть на классы sf::FileInputStream и sf::MemoryInputStream.

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

Распространенные заблуждения


Некоторые классы ресурсов не загружаются полностью после вызова loadFromStream. Вместо этого, они продолжают читать данные из источника данных так долго, как это требуется. По этой причине класс sf: Music читает аудио примитивы через поток на протяжении их проигрывания, а класс sf: Font загружает глифы, когда в этом возникает необходимость.

Как следствие, поток, используемый вами для загрузки музыки или шрифта, также, как и источник данных, должны существовать так долго, как долго эти ресурсы используются. Разрушение источника информации или класса потока данных приводит к неопределенному поведению (программа может завершить свое выполнение, повредить данные, а может ничего не произойти).

Другим распространенным заблуждением является возврат функцией неверного значения, т.е. значения, возврат которого не ожидается SFML в данной ситуации. Например, для sf::FileInputStream можно было бы написать функцию поиска следующим образом:

sf::Int64 FileInputStream::seek(sf::Int64 position)
{
    return std::fseek(m_file, position, SEEK_SET);
}


Однако этот код не верен, потому что std::fseek возвращает нуль при успехе, в то время как SFML ожидает возврата новой позиции в потоке.

© Habrahabr.ru