Я пишу плагин FUSE в C. Я отслеживаю структуры данных в файловой системе через структуры как:
typedef struct {
block_number_t inode;
filename_t filename; //char[SOME_SIZE]
some_other_field_t other_field;
} fs_directory_table_item_t;
Очевидно, я должен читать (пишут) эти структуры из (к) диску в какой-то момент. Я мог рассматривать структуру как последовательность байтов и сделать что-то вроде этого:
read(disk_fd, directory_table_item, sizeof(fs_directory_table_item_t));
... за исключением того, что не может возможно работать как filename
на самом деле указатель на char
массив.
Я действительно хотел бы избежать необходимости писать код как:
read(disk_df, *directory_table_item.inode, sizeof(block_number_t));
read(disk_df, directory_table_item.filename, sizeof(filename_t));
read(disk_df, *directory_table_item.other_field, sizeof(some_other_field_t));
... для каждой структуры в коде, потому что я должен был бы копировать код и изменения в не менее чем трех различных местах (определение, чтение, пишущий).
Какая-либо СУШИЛКА, но все еще удобные в сопровождении идеи?
Память строки будет частью вашей структуры, даже если тип массива повышен до указателя во многих случаях, тип, хранящийся в структуре, - это массив, а не указатель.
typedef struct {
block_number_t inode;
filename_t filename; //char[SOME_SIZE]
some_other_field_t other_field;
} fs_directory_table_item_t;
Итак, ваш оператор чтения:
read(disk_fd, directory_table_item, sizeof(fs_directory_table_item_t));
будет работать и вводить данные.
При чтении и записи блоков памяти следует учитывать заполнение . Заполнение - это дополнительные пустые поля, добавленные компилятором для выравнивания данных по соответствующим границам; например 32-байтовое значение часто должно начинаться с 4-байтовой границы в памяти, чтобы процессор мог эффективно читать его. Обычно это не повод для беспокойства, но при сохранении структуры на диск могут возникнуть проблемы, если вы перекомпилируете код с другим параметром. Часто есть какие-то директивы #pragma
, которые отключают заполнение, я думаю, что в MS Visual c ++ он называется #pragma pack
.
Один из способов сделать это - создать статические константные таблицы данных, которые описывают ваши структуры, чтобы с ними мог работать простой механизм чтения / записи.
Вам необходимо определить структуру, которая может представлять все, что вам нужно знать для чтения или записи одного поля одной структуры.
typedef struct {
char * name;
size_t offset;
size_t size;
int format_as;
void* format_struct; // if format_as & IS_STRUCT, this is the structure type
} field_info_t
enum {
AS_CHAR =1,
AS_SHORT,
AS_LONG,
// add other types here
AS_MASK = 0xFF,
// these flags can be OR'd with type to refine the behavior
IS_POINTER = 0x100,
IS_STRUCT = 0x200,
};
Затем создайте из них таблицы, которые описывают все ваши структуры данных.
#define FIELD_OFF(type, field) ((size_t)(LONG_PTR)&(((type *)0)->field))
#define FIELD_SIZE(type, field) (sizeof(((type *)0)->field))
static const field_info_t g_fs_directory_table_item_table[] = {
{ "inode",
FIELD_OFF(fs_directory_table_item_t, inode),
FIELD_SIZE(fs_directory_table_item_t, inode),
AS_LONG,
NULL
},
{ "filename",
FIELD_OFF(fs_directory_table_item_t, filename),
sizeof(filename_t),
AS_CHAR | IS_POINTER,
NULL
},
{ "other_field",
FIELD_OFF(fs_directory_table_item_t, other_field),
FIELD_SIZE(fs_directory_table_item_t, other_field),
AS_STRUCT,
&some_other_field_table,
},
};
И затем механизмы чтения и записи, которые принимают указатель на структуру и указатель на таблицу, описывающую структуру, и считывают / записывают различные поля.
void ReadStructure(FILE * fh, void * pStruct, field_info_t * pFields, int num_fields)
{
// this is just a rough sketch of the code.
for (int ii = 0; ii < num_fields; ++ii)
{
int * field_size = pFields[ii].size;
char * pfield = (char*)pStruct + pFields[ii].offset;
if (pFields[ii].format_as & AS_POINTER)
pfield = *(char**)pfield;
switch (pFields[ii].format_as & AS_MASK)
{
case AS_CHAR:
....
}
}
}
void WriteStructure(FILE * fh, void * pStruct, field_info_t * pFields, int num_fields);
Вам все равно придется поддерживать массив field_info_t
для каждой из ваших структур данных, но как только он у вас есть, вы можете читать, писать, проверять и распечатывать свои данные с помощью набора достаточно простые функции.