MIT ライセンスを備えた使いやすいヘッダーのみのクロスプラットフォーム C++11 メモリ マッピング ライブラリ。
mio は、Boost を使用せずにメモリ マップド ファイル IO を必要とする C++ プロジェクトに簡単に組み込める (つまり、依存関係がない) ことを目的として作成されました。
遠慮なく問題を開いてください。あらゆる懸念にできる限り対処するよう努めます。
メモリマッピングはスライスされたパン以来最高の機能だからです。
さらに深刻なことに、Boost.Iostreams を使用する代わりにこのライブラリを作成した主な動機は、すでに開いているファイル ハンドル/記述子を使用してメモリ マッピングを確立するためのサポートが欠如していたことでした。 mioならそれが可能です。
さらに、Boost.Iostreams のソリューションでは、ユーザーがページ境界でオフセットを正確に選択する必要がありますが、これは面倒でエラーが発生しやすくなります。一方、mio はこれを内部で管理し、オフセットを受け入れて最も近いページ境界を見つけます。
細かい点ではありますが、Boost.Iostreams は、必要でない場合でも共有セマンティクスを提供するためにstd::shared_ptr
を使用してメモリ マップト ファイル IO を実装しており、ヒープ割り当てのオーバーヘッドは不要であるか望ましくない可能性があります。 mio には、2 つのユースケースをカバーする 2 つのクラスがあります。1 つは移動専用 (基本的にシステム固有の mmapping 関数に対するゼロコストの抽象化) で、もう 1 つは Boost.Iostreams の対応物と同様に動作します。共有セマンティクス。
注: マッピングを作成する前にファイルが存在している必要があります。
ファイルをメモリにマップするには、次の 3 つの方法があります。
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
out パラメーターを介して報告されます。
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
は移動専用ですが、同じマッピングへの複数のコピーが必要な場合は、 std::shared_ptr
セマンティクスを持ち、 mio::basic_mmap
mio::basic_shared_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 >;
ただし、C++17 で新しいstd::byte
型を使用する場合など、独自の型を定義すると便利な場合があります。
using mmap_source = mio::basic_mmap_source<std::byte>;
using mmap_sink = mio::basic_mmap_sink<std::byte>;
通常は必要ありませんが、 mio はユーザーが要求したオフセットをページ境界にマップするため、 mio/page.hpp
にあるmio::page_size()
を呼び出すことで、基礎となるシステムのページ割り当て粒度をクエリできます。
single_includemiomio.hpp
を含めるだけで、Mio を単一のヘッダー ファイルとしてプロジェクトに追加できます。単一ヘッダー ファイルはthird_party
内でamalgamate.py
スクリプトを実行することでいつでも再生成できます。
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
もちろん、構成ステップ中に生成されたプロジェクト ファイルを開いた後、IDE 内からビルド ステップとテストステップをそれぞれallターゲットとテストターゲットを介して実行することもできます。
Mio のテストは、CDash ソフトウェア品質ダッシュボード アプリケーションのクライアントとして動作するように構成されています。この動作モードの詳細については、Kitware のドキュメントを参照してください。
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 プロジェクトがfind_package
経由で mio を消費できるようになります。
find_package ( mio REQUIRED )
target_link_libraries ( MyTarget PUBLIC mio::mio )
WINDOWS ユーザー: mio::mio
ターゲットの#define
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 を使用してバージョン管理されている場合、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 インクルード ディレクトリが追加されます。
add_subdirectory ( path /to/mio/ )
target_link_libraries ( MyTarget PUBLIC <mio::mio | mio> )
サブプロジェクトとして、mio のテストとサンプルはビルドされず、CPack の統合はホスト プロジェクトに延期されることに注意してください。