Простая в использовании кросс-платформенная библиотека отображения памяти C++11 только для заголовков с лицензией MIT.
mio был создан с целью обеспечить возможность легкого включения (т. е. отсутствия зависимостей) в любой проект C++, которому требуется ввод-вывод файла с отображением в памяти, без необходимости использования Boost.
Пожалуйста, не стесняйтесь открывать вопрос, я постараюсь решить любые проблемы, насколько смогу.
Потому что отображение памяти — лучшая вещь со времен нарезанного хлеба!
Если серьезно, основной мотивацией для написания этой библиотеки вместо использования Boost.Iostreams было отсутствие поддержки установления сопоставления памяти с уже открытым дескриптором/дескриптором файла. Это возможно с помощью mio.
Более того, решение Boost.Iostreams требует, чтобы пользователь выбирал смещения точно на границах страницы, что является обременительным и чревато ошибками. mio, с другой стороны, управляет этим внутренне, принимая любое смещение и находя ближайшую границу страницы.
Несмотря на незначительную придирку, Boost.Iostreams реализует ввод-вывод файла с отображением в памяти с помощью std::shared_ptr
для обеспечения общей семантики, даже если она не нужна, и накладные расходы на выделение кучи могут быть ненужными и/или нежелательными. В mio есть два класса, охватывающие два варианта использования: один предназначен только для перемещения (по сути, это абстракция с нулевой стоимостью над специфичными для системы функциями отображения), а другой действует точно так же, как его аналог Boost.Iostreams, с общая семантика.
ПРИМЕЧАНИЕ. Файл должен существовать до создания сопоставления.
Существует три способа отображения файла в памяти:
std::system_error
: mio::mmap_source mmap (path, offset, size_to_map);
или вы можете опустить аргументы offset
и size_to_map
, и в этом случае будет отображен весь файл:
mio::mmap_source mmap (path);
std::error_code error;
mio::mmap_source mmap = mio::make_mmap_source(path, offset, size_to_map, error);
или:
mio::mmap_source mmap = mio::make_mmap_source(path, error);
map
: std::error_code error;
mio::mmap_source mmap;
mmap.map(path, offset, size_to_map, error);
или:
mmap.map(path, error);
ПРИМЕЧАНИЕ. Конструкторы требуют включения исключений. Если вы предпочитаете создавать свои проекты с -fno-exceptions
, вы все равно можете использовать другие способы.
Более того, в каждом случае вы можете указать либо какой-либо строковый тип для пути к файлу, либо использовать существующий действительный дескриптор файла.
# include < sys/types.h >
# include < sys/stat.h >
# include < fcntl.h >
# include < mio/mmap.hpp >
// #include <mio/mio.hpp> if using single header
# include < algorithm >
int main ()
{
// NOTE: error handling omitted for brevity.
const int fd = open ( " file.txt " , O_RDONLY);
mio::mmap_source mmap (fd, 0 , mio::map_entire_file);
// ...
}
Однако mio не проверяет, имеет ли предоставленный файловый дескриптор те же права доступа, что и желаемое сопоставление, поэтому сопоставление может завершиться неудачей. О таких ошибках сообщается через выходной параметр std::error_code
, который передается функции сопоставления.
ПОЛЬЗОВАТЕЛИ WINDOWS : эта библиотека поддерживает использование расширенных типов символов для функций, в которых ожидаются строки символов (например, параметры пути).
# include < mio/mmap.hpp >
// #include <mio/mio.hpp> if using single header
# include < system_error > // for std::error_code
# include < cstdio > // for std::printf
# include < cassert >
# include < algorithm >
# include < fstream >
int handle_error ( const std::error_code& error);
void allocate_file ( const std::string& path, const int size);
int main ()
{
const auto path = " file.txt " ;
// NOTE: mio does *not* create the file for you if it doesn't exist! You
// must ensure that the file exists before establishing a mapping. It
// must also be non-empty. So for illustrative purposes the file is
// created now.
allocate_file (path, 155 );
// Read-write memory map the whole file by using `map_entire_file` where the
// length of the mapping is otherwise expected, with the factory method.
std::error_code error;
mio::mmap_sink rw_mmap = mio::make_mmap_sink (
path, 0 , mio::map_entire_file, error);
if (error) { return handle_error (error); }
// You can use any iterator based function.
std::fill (rw_mmap. begin (), rw_mmap. end (), ' a ' );
// Or manually iterate through the mapped region just as if it were any other
// container, and change each byte's value (since this is a read-write mapping).
for ( auto & b : rw_mmap) {
b += 10 ;
}
// Or just change one value with the subscript operator.
const int answer_index = rw_mmap. size () / 2 ;
rw_mmap[answer_index] = 42 ;
// Don't forget to flush changes to disk before unmapping. However, if
// `rw_mmap` were to go out of scope at this point, the destructor would also
// automatically invoke `sync` before `unmap`.
rw_mmap. sync (error);
if (error) { return handle_error (error); }
// We can then remove the mapping, after which rw_mmap will be in a default
// constructed state, i.e. this and the above call to `sync` have the same
// effect as if the destructor had been invoked.
rw_mmap. unmap ();
// Now create the same mapping, but in read-only mode. Note that calling the
// overload without the offset and file length parameters maps the entire
// file.
mio::mmap_source ro_mmap;
ro_mmap. map (path, error);
if (error) { return handle_error (error); }
const int the_answer_to_everything = ro_mmap[answer_index];
assert (the_answer_to_everything == 42 );
}
int handle_error ( const std::error_code& error)
{
const auto & errmsg = error. message ();
std::printf ( " error mapping file: %s, exiting... n " , errmsg. c_str ());
return error. value ();
}
void allocate_file ( const std::string& path, const int size)
{
std::ofstream file (path);
std::string s (size, ' 0 ' );
file << s;
}
mio::basic_mmap
предназначен только для перемещения, но если требуется несколько копий одного и того же сопоставления, используйте mio::basic_shared_mmap
, который имеет семантику std::shared_ptr
и имеет тот же интерфейс, что и mio::basic_mmap
.
# include < mio/shared_mmap.hpp >
mio::shared_mmap_source shared_mmap1 ( " path " , offset, size_to_map);
mio::shared_mmap_source shared_mmap2 (std::move(mmap1)); // or use operator=
mio::shared_mmap_source shared_mmap3 (std::make_shared<mio::mmap_source>(mmap1)); // or use operator=
mio::shared_mmap_source shared_mmap4;
shared_mmap4.map( " path " , offset, size_to_map, error);
Можно определить тип байта (который должен быть той же ширины, что и char
), хотя псевдонимы для наиболее распространенных из них предоставляются по умолчанию:
using mmap_source = basic_mmap_source< char >;
using ummap_source = basic_mmap_source< unsigned char >;
using mmap_sink = basic_mmap_sink< char >;
using ummap_sink = basic_mmap_sink< unsigned char >;
Но может быть полезно определить ваши собственные типы, скажем, при использовании нового типа std::byte
в C++17:
using mmap_source = mio::basic_mmap_source<std::byte>;
using mmap_sink = mio::basic_mmap_sink<std::byte>;
Хотя обычно это не требуется, поскольку пользователи mio отображают запрошенные смещения границ страниц, вы можете запросить степень детализации выделения страниц базовой системы, вызвав mio::page_size()
, который находится в mio/page.hpp
.
Mio можно добавить в ваш проект как один заголовочный файл, просто включив single_includemiomio.hpp
. Отдельные файлы заголовков можно создать повторно в любое время, запустив сценарий amalgamate.py
внутри third_party
.
python amalgamate.py -c config.json -s ../include
Будучи библиотекой только заголовков, mio не имеет скомпилированных компонентов. Тем не менее, предусмотрена система сборки CMake, позволяющая легко тестировать, устанавливать и составлять подпроекты на многих платформах и операционных системах.
Mio распространяется с небольшим набором тестов и примеров. Если mio настроен как проект CMake самого высокого уровня, этот набор исполняемых файлов создается по умолчанию. Исполняемые файлы тестов Mio интегрированы с программой тестового драйвера CMake CTest.
CMake поддерживает ряд бэкэндов для компиляции и компоновки.
Чтобы использовать инструмент построения статической конфигурации, например GNU Make или Ninja:
cd < mio source directory >
mkdir build
cd build
# Configure the build
cmake -D CMAKE_BUILD_TYPE= < Debug | Release >
-G < " Unix Makefiles " | " Ninja " > ..
# build the tests
< make | ninja | cmake --build . >
# run the tests
< make test | ninja test | cmake --build . --target test | ctest >
Чтобы использовать инструмент построения динамической конфигурации, например Visual Studio или Xcode:
cd < mio source directory >
mkdir build
cd build
# Configure the build
cmake -G < " Visual Studio 14 2015 Win64 " | " Xcode " > ..
# build the tests
cmake --build . --config < Debug | Release >
# run the tests via ctest...
ctest --build-config < Debug | Release >
# ... or via CMake build tool mode...
cmake --build . --config < Debug | Release > --target test
Конечно, этапы сборки и тестирования также можно выполнить с помощью целевых объектов all и test соответственно из среды IDE после открытия файла проекта, созданного на этапе настройки.
Тестирование Mio также настроено для работы в качестве клиента для приложения панели мониторинга качества программного обеспечения CDash. Дополнительную информацию об этом режиме работы см. в документации комплектного ПО.
Система сборки Mio обеспечивает цель установки и поддержку последующего использования с помощью встроенной функции CMake find_package
. CMake допускает установку в произвольное место, которое можно указать, определив CMAKE_INSTALL_PREFIX
во время настройки. При отсутствии спецификации пользователя CMake установит mio в обычное место в зависимости от операционной системы платформы.
Чтобы использовать инструмент построения статической конфигурации, например GNU Make или Ninja:
cd < mio source directory >
mkdir build
cd build
# Configure the build
cmake [-D CMAKE_INSTALL_PREFIX = " path/to/installation " ]
[-D BUILD_TESTING = False]
-D CMAKE_BUILD_TYPE=Release
-G < " Unix Makefiles " | " Ninja " > ..
# install mio
< make install | ninja install | cmake --build . --target install >
Чтобы использовать инструмент построения динамической конфигурации, например Visual Studio или Xcode:
cd < mio source directory >
mkdir build
cd build
# Configure the project
cmake [-D CMAKE_INSTALL_PREFIX = " path/to/installation " ]
[-D BUILD_TESTING = False]
-G < " Visual Studio 14 2015 Win64 " | " Xcode " > ..
# install mio
cmake --build . --config Release --target install
Обратите внимание, что последняя команда последовательности установки может потребовать прав администратора (например, sudo
), если корневой каталог установки находится за пределами вашего домашнего каталога.
Эта установка
include/mio
корня установкиshare/cmake/mio
в корне установки. Этот последний шаг позволяет последующим проектам CMake использовать mio через find_package
, например
find_package ( mio REQUIRED )
target_link_libraries ( MyTarget PUBLIC mio::mio )
ПОЛЬЗОВАТЕЛИ WINDOWS : цель mio::mio
#define
s WIN32_LEAN_AND_MEAN
и NOMINMAX
. Первый гарантирует, что импортируемая поверхность Win API минимальна, а второй отключает макросы Windows min
и max
, чтобы они не мешали std::min
и std::max
. Поскольку mio — это библиотека только заголовков, эти определения будут проникать в последующие сборки CMake. Если их присутствие вызывает проблемы в вашей сборке, вы можете использовать альтернативную цель mio::mio_full_winapi
, которая не добавляет ни одного из этих определений.
Если mio был установлен в нестандартное место, для последующих проектов может потребоваться указать корневой каталог установки mio через
CMAKE_PREFIX_PATH
,CMAKE_PREFIX_PATH
илиmio_DIR
.Дополнительную информацию см. в документации по комплектному ПО.
Кроме того, mio поддерживает пакетные перемещаемые установки через CPack. После настройки из каталога сборки вызовите cpack следующим образом, чтобы создать упакованную установку:
cpack -G < generator name > -C Release
Список поддерживаемых генераторов варьируется от платформы к платформе. Полный список поддерживаемых генераторов на вашей платформе смотрите в выводе cpack --help
.
Чтобы использовать mio в качестве подпроекта, скопируйте репозиторий mio в папку Dependency/Externals вашего проекта. Если ваш проект контролируется версиями с помощью git, для синхронизации с репозиторием updstream можно использовать подмодуль git или поддерево git. Использование и относительные преимущества этих средств git выходят за рамки этого документа, но вкратце каждый из них можно описать следующим образом:
# via git submodule
cd < my project ' s dependencies directory>
git submodule add -b master https://github.com/mandreyel/mio.git
# via git subtree
cd <my project ' s root directory >
git subtree add --prefix < path/to/dependencies > /mio
https://github.com/mandreyel/mio.git master --squash
Учитывая подкаталог mio в проекте, просто добавьте следующие строки в свой проект, чтобы добавить каталоги mio include в путь включения вашей цели.
add_subdirectory ( path /to/mio/ )
target_link_libraries ( MyTarget PUBLIC <mio::mio | mio> )
Обратите внимание, что тесты и примеры mio в качестве подпроекта не будут создаваться, а интеграция CPack будет отложена до основного проекта.