مكتبة خرائط ذاكرة C++ 11 عبر الأنظمة الأساسية سهلة الاستخدام مع ترخيص MIT.
تم إنشاء mio بهدف سهولة تضمينه (أي عدم وجود تبعيات) في أي مشروع C++ يحتاج إلى ملف IO المعين للذاكرة دون الحاجة إلى سحب Boost.
لا تتردد في فتح مشكلة، وسأحاول معالجة أي مخاوف قدر الإمكان.
لأن رسم خرائط الذاكرة هو أفضل شيء منذ شرائح الخبز!
والأخطر من ذلك، أن الدافع الأساسي لكتابة هذه المكتبة بدلاً من استخدام Boost.Iostreams، كان عدم وجود دعم لإنشاء تعيين للذاكرة باستخدام مقبض/واصف ملف مفتوح بالفعل. هذا ممكن مع ميو.
علاوة على ذلك، يتطلب حل Boost.Iostreams أن يقوم المستخدم باختيار الإزاحات عند حدود الصفحة بالضبط، وهو أمر مرهق وعرضة للخطأ. من ناحية أخرى، يدير mio هذا الأمر داخليًا، ويقبل أي إزاحة ويبحث عن أقرب حد للصفحة.
على الرغم من وجود خطأ بسيط، فإن Boost.Iostreams ينفذ ملف IO المعين للذاكرة باستخدام std::shared_ptr
لتوفير دلالات مشتركة، حتى لو لم تكن هناك حاجة إليها، وقد يكون الحمل الزائد لتخصيص الكومة غير ضروري و/أو غير مرغوب فيه. في mio، هناك فئتان لتغطية حالتي الاستخدام: واحدة للتحرك فقط (في الأساس تجريد بدون تكلفة على وظائف mmapping الخاصة بالنظام)، والأخرى تعمل تمامًا مثل نظيرتها 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
باعتبارها مكتبة رأسية فقط، لا تحتوي ميو على مكونات مجمعة. ومع ذلك، يتم توفير نظام إنشاء 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 بعد فتح ملف المشروع الذي تم إنشاؤه أثناء خطوة التكوين.
تم أيضًا تكوين اختبار Mio للعمل كعميل لتطبيق لوحة معلومات جودة برنامج CDash. الرجاء مراجعة وثائق Kitware لمزيد من المعلومات حول وضع التشغيل هذا.
يوفر نظام البناء الخاص بـ Mio هدف تثبيت ودعمًا للاستهلاك النهائي عبر وظيفة find_package
الجوهرية الخاصة بـ CMake. يسمح 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 ضئيلة، بينما يقوم الأخير بتعطيل وحدات الماكرو min
و max
الخاصة بنظام Windows حتى لا تتداخل مع std::min
و std::max
. نظرًا لأن mio هي مكتبة رأسية فقط، فسوف تتسرب هذه التعريفات إلى إصدارات CMake النهائية. إذا كان وجودها يسبب مشاكل في الإصدار الخاص بك، فيمكنك استخدام الهدف البديل mio::mio_full_winapi
، الذي لا يضيف أيًا من هذه التعريفات.
إذا تم تثبيت mio في موقع غير تقليدي، فقد يكون من الضروري للمشروعات النهائية تحديد الدليل الجذر لتثبيت mio عبر إما
CMAKE_PREFIX_PATH
،CMAKE_PREFIX_PATH
، أوmio_DIR
.الرجاء مراجعة وثائق Kitware لمزيد من المعلومات.
بالإضافة إلى ذلك، يدعم mio عمليات التثبيت المجمعة القابلة للنقل عبر CPack. بعد التكوين، من دليل البناء، قم باستدعاء cpack كما يلي لإنشاء تثبيت معبأ:
cpack -G < generator name > -C Release
تختلف قائمة المولدات المدعومة من منصة إلى أخرى. راجع مخرجات cpack --help
للحصول على قائمة كاملة بالمولدات المدعومة على النظام الأساسي الخاص بك.
لاستخدام mio كمشروع فرعي، انسخ مستودع mio إلى مجلد التبعيات/الخارجية الخاص بمشروعك. إذا كان إصدار مشروعك يتم التحكم فيه باستخدام git، فيمكن استخدام وحدة git الفرعية أو شجرة git الفرعية للمزامنة مع مستودع updstream. إن الاستخدام والمزايا النسبية لمرافق 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 إلى المشروع المضيف.