CrawlCatcher
Сеть твоих интересов! Сообщайся!
Вы на сайте:
Senseti Inc.
   
Блоги

Для более удобной работы с данными и памятью при выполнении задач по С++ я написал простой класс работы с файлами и памятью (использующий механизмы «MemoryMappedFiles»). Думаю, использование этого класса, упростит написание домашних заданий. Кроме того на примере этого класса (точнее классов) я хотел бы продемонстрировать красоту и удобство С++.

1. Memory mapped (это не С++)

Механизм ОС «MemoryMappedFiles» очень удобен, поскольку позволяет работать с файлом как с областью в ОЗУ (файл открывается, затем «проецируется» на область ОЗУ, после чего с содержимым файла можно работать так, как будто это просто область памяти: напрямую (через указатели) обращаться к нужным байтами и битам). При этом механизмы ОС обеспечивают эффективную буферизацию-кэширование - ведь с точки зрения ОС MemoryMappedFiles это «стандартный» механизм «виртуальной памяти».
В Windows «проекция» создается вызовом 3х функций

// 1) Открыть (создать) файл
hFile = CreateFile(pszFileName, ...

// 2) Создать для файла объект ядра "проекция файла"
hFileMapping = CreateFileMapping(hFile, ...

// 3) Проецировать файл на область адресов ОЗУ и отдать указатель на начало блока
pBuf = (char*) MapViewOfFile(hFileMapping, ...

После выполнения этих трех шагов с pBuf можно работать как с «обычным» блоком байтов (например, массивом char-ов, или, например, - int-ов). Размер этого блока будет совпадать с размером файла.

Собственно эти 3 действия выполняются в конструкторе класса SimpleFileBuf. В конструктор передаются два параметра: Название файла (который будет спроецирован в память) и требуемый размер файла (если указать 0, то будет взят фактический размер самого файла на диске). Если файл не существовал - он будет создан.

Таким образом, вызов конструктора сразу «раскладывает» файл в памяти.


Но удобен этот класс не только этим ...

2. SimpleFileBuf еще и указатель!


Объект SimpleFileBuf является еще и указателем (!). Правда, если разобраться «по совести» ничего необычного или неправильно в этом нет, ведь по сути дела SimpleFileBuf является «держателем» области памяти и указателем на эту область памяти. Раз так то, было бы очень удобным использовать его так же как «обычный» указатель: с помощью [] адресоваться к нужной ячейке массива, копировать данные обычными операциями типа memcpy или strcpy или простой операцией «=».

Это достигается перегрузкой оператора «*». Таким вот объявлением:

operator char * ( void ) { return pBuf;}
Благодаря этому объявлению с объектами типа SimpleBuf можно работать как полноценными указателями - абсолютно также как с «простыми» char*

Например:

// открываем файл на чтение
SimpleFileBuf sfb1( "d:\\test1.htm" );

// второй файл открываем (или создаем, если его не было) чтобы туда писать
SimpleFileBuf sfb2( "d:\\test2.htm", sfb1.size( ) );


char* p1 = sfb1+7; // можем перемещаться по файлу как по простому массиву

char p2 = sfb1[7]; // читать любые данные из любых ячеек - так как будто это обычный массив

//записывать любые данные в любые ячейки в файлах
sfb2[0]='2';
sfb2[1]=p2;


// работать частями файлов как с обычными строками
strncpy( sfb2+12, sfb1, 1919 );


Такое вот элегантное использование рукотворного объекта как указателя не только красиво. Оно обеспечивает более понятный и простой, а значит и более надежный код. Кроме того предоставляет классные возможности при написании обобщенных алгоритмов (templates).

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

3. Автоматическая сборка мусора бывает не только в .NET (С++ умеет это куда как лучше).

В третьих класс SimpleFileBuf реализует «автоматическую сборку мусора», поскольку написан в парадигме «автоматических объектов» (захват ресурсов есть инициализация и наоборот - инициализация это захват ресурсов).

При использовании автоматических объектов типа SimpleFileBuf не нужно беспокоиться о пресловутой «сборке мусора» (которой пугают девственных несовершеннолетних программистов, пытаясь не пустить их в Африку гулять писать на С++).


После выхода из области видимости автоматического объекта SimpleFileBuf все ресурсы будут автоматически освобождены (файл закрыт, память возвращена ОС). Более того, ресурсы будут возвращены и при «нехороших поворотах» - например, при выстреливании exceptions.

Т.е. это не просто полноценный указатель. Это еще и «умный» указатель, умеющий автоматически освобождать ресурсы, когда они перестают быть нужными.

Но и это еще не все.

4. Как говорят у нас в деревне ... раз пошла такая пьянка ...

Несу последний огурец Упомяну еще об одном аспекте. В классе SimpleFileBuf используется другой автоматический объект AutoHandle. Это объект wrapper (обертка) над «дескриптором объекта Windows». Его задача сделать из низкоуровневого указателя HANDLE умный указатель умеющий автоматически освобождать ресурсы


Вы наверное помните что любой объект Windows имеет свой «дескриптор» (уникальный числовой идентификатор данного объекта, фактически индекс этого объекта в центральной таблице указателей «Бургомистр»).

Дескрипторы получают все объекты: открываем файл - Windows возвращает нам дескриптор (HANDLE) этого объекта. Создаем мьютекс - Windows возвратит нам HANDLE этого mutex-a. Когда понадобится сделать какуюто операцию с объектом нам понадобится передать HANDLE объекта при вызове системной функции.

HANDLE hFile = CreateFile(pszFileName, ...
ReadFile(hFile, ...
WriteFile(hFile, ...
CheckFile(hFile, ...
CloseFile(hFile, ...

Это удобно. Но есть одно «но». HANDLE это по сути дела «низкоуровневый указатель» и если мы забудем его закрыть, то это приведет к потере-«подвисанию» ресурсов OS.


А оказаться в ситуации «потери-подвисания» HANDLE проще простого. Например, при создании MemoryMappedFile нужно выполнить три действия: открыть холодильник, вытащить бегемота, положить жирафа, закрыть холодильник последовательно вызвать функции CreateFile, CreateFileMapping, MapViewOfFile каждая из которых создает объект HANDLE и каждая из которых (по закону пакости - последняя) может окончиться с ошибкой.

Поскольку эти действия производятся в конструкторе, то, в соответствии с концепцией «инициализация есть захват ресурсов» на них нужно реагировать весьма брутально - выстреливать exception (а иначе как выйти из конструктора при неудачном создании объекта?).

Но такой «брутальный выход» чреват либо подвисанием уже выделенных HANDLE либо необходимостью писать идиотские гирлянды проверок перед каждым exception.

Собственно эту дилемму решает wrapper-class AutoHandle.

Во первых он реализует дисциплину автоматического объекта, гарантируя освобождение ресурсов в любой ситуации.

Во вторых коде вторых он «выглядит» как обычный HANDLE. Так, например, если посмотреть в код конструктора SimpleFileBuf то можно увидеть такие вот вызовы:

hFile = CreateFile( FileName, (GENERIC_READ | GENERIC_WRITE), . . .
MaximumSize = GetFileSize( hFile, . . .
hFileMapping = CreateFileMapping( hFile, NULL, PAGE_READWRITE, . . .
pBuf = (char*) MapViewOfFile( hFileMapping, FILE_MAP_ALL_AC . . .

... обычные вызовы обычных функций Windows, работающих с HANDLE


Только вот hFile это «рукописный объект» типа AutoHandle а не HANDLE. И ничего - все работает. Это получилось благодаря перегрузке операций. А именно:
1) Оператора присваивания operator HANDLE ( void );
2) Оператора преобразования типа AutoHandle & operator = ( HANDLE const & hSrc );


Благодаря этим конструкциям компилятор «разворачивает» вызовы, приведенные выше в

hFile.hHandle = CreateFile( FileName, . . .
MaximumSize = GetFileSize( hFile.hHandle, . . .
hFileMapping.hHandle = CreateFileMapping( hFile.hHandle, . . .
pBuf = (char*) MapViewOfFile( hFileMapping.hHandle, . . .

И здесь опять мы получаем простой, понятный и привычный код, который к тому же защищен от «потери-подвисания» ресурсов. Так, например, при любом exception в конструкторе будут обязательно вызваны деструкторы инициализированных объектов HANDLE.

Надеюсь, Вы дочитали этот текст до этого предложения. Если так - примите мое искреннее уважение. Теперь дело за малым - взять класс (он в прицепе (аттаче) к этой статье) и использовать его.

Удачи!


P.S.: Я решил разместить эту запись в блоге, поскольку на сегодняшнем этапе изучения С++, этот материал важен для вас и я буду ссылаться на него еще не раз. Но с другой стороны я надеюсь, что уже скоро эта запись станет приниматься как 2х2=4 и будет теплым напоминанием о «первых шагах и шишках» в С++.

Удачи нам !


20.11.2010, 23:53
Просмотров: 429
 | 
 | 
     
 | 
Голосов: 2
 | 
Комментарии:


Для добавления комментария, вам необходимо войти или зарегистрироваться
Request Time: 1.709s.