С перегрузками операторов C++ можно создать класс, который эмулирует тип указателя, поэтому я экспериментировал с абстрагированием операций чтения с помощью этого подхода.
class FilePointer
{
FILE* file;
size_t position;
FilePointer (FILE* file, size_t position)
{
this->file = file;
this->position = position;
};
public:
FilePointer (FILE* file)
{
this->file = file;
this->position = 0;
};
FilePointer operator+ (size_t offset)
{
return FilePointer(file,position + offset);
};
FilePointer operator++ () // Prefix : ++FilePointer
{
position++;
return *this;
};
FilePointer operator++ (int) // Postfix : FilePointer++
{
FilePointer before = *this;
position++;
return before;
};
FilePointer operator+= (size_t offset)
{
position += offset;
return *this;
};
uint8_t operator* ()
{
fseek(file,position,SEEK_SET);
Uint8 out;
fread(&out,1,1,file);
return out;
};
uint8_t operator[] (size_t offset)
{
return *(*this + offset);
};
};
Как видно из приведенного выше фрагмента кода, я смог выяснить, как различать варианты оператора приращения, чтобы данные FILE
FilePointer f;
и f++
вели себя интуитивно.
Что, если я хочу использовать этот класс для файла пишет? В настоящее время я могу захватить байт ++f
и это работает, но если я хочу что-то "установить", т.е. uint8_t something = f[0];
, перегрузки как таковые для этого не подойдут.
Помимо того, является ли это «хорошей практикой» (хотя не стесняйтесь взвешивать и это), есть ли способ реализовать это поведение в перегрузках операторов для класса?
uint8_t n = f[0];
n++;
f[0] = n;
Или, что еще интереснее, что-то вроде этого:
f[1]++;
Один из способов, которым я представляю, что это можно сделать, - реализовать еще один класс, который представляет "разыменованный FilePointer", но было любопытно, возможно ли это сделать только с перегрузками в самом классе FilePointer.
Возврат прокси — единственный способ сделать это: выражение f[x]
будет оцениваться первым и должно оцениваться как что-то, для которого f[x] = 100
или ++f[x]
или что-то другое имеет смысл. operator[]
не может ничего узнать о выражении, в котором вызывается is (кроме того, является ли f
константной ссылкой или чем-то еще)
Вы знаете о эта почта, не так ли?
Кажется более практичным добавить управление итератором, а не управление []
One way I imagine it could be done is by implementing yet another class which represents a "dereferenced FilePointer"
Это почти единственный способ сделать это. Это будет объект «прокси-типа», который может быть преобразован в uint8_t
(возможно, неявно в таком случае, хотя в целом я ненавижу неявные преобразования), но также может быть назначен.
Он должен быть построен с помощью FilePointer*
и быть friend
, чтобы он мог вызывать соответствующую функцию «записи».
Однако это будет немного запутанно, потому что он может очень быстро «зависнуть», писать не в том месте. Вы мог также сохраняете текущую позицию во время создания прокси-объекта и выполняете возврат к этой позиции если, необходимой на момент написания. Но все эти поиски не очень эффективны.
Также чтение по одному байту за раз происходит медленно.
В целом, хотя ваша попытка замечательна, я бы посоветовал не использовать этот подход в целом. Вы можете по крайней мере обернуть FILE*
в хороший класс, который сделает fclose
за вас, если он выйдет за рамки.
but was curious if it is possible to do with only the overloads in the FilePointer class itself.
Я так не думаю. Все, что вы возвращаете из operator[]
, нуждается в каком-то магическом состоянии, которое может принимать значение для «установки» и подключения к механизму FILE*
. Возврат uint8_t
просто никогда не позволит вам сделать это.
@SombreroChicken :P
Имеет много смысла, в том числе и о производительности. Обычно я использовал стандартный файловый ввод-вывод C напрямую (поэтому вы видите FILE*
в этом классе), и мне было просто любопытно в C++, что потребуется, чтобы полностью абстрагироваться от файлового ввода-вывода, как это. В принципе, я не планирую использовать это в базе производственного кода, но было бы неплохо увидеть, что вам сойдет с рук в языке.
Другой подход к повышению производительности записи в 1 символ может заключаться в том, что ваш указатель прокси-файла на самом деле является буфером, который каким-то образом собирает ряд назначений, а затем загружает их все в файл при уничтожении.
При использовании прокси-подхода следует помнить, что вызывающая сторона может удерживать несколько ваших прокси-объектов, используя auto
или auto&
, и пытаться обновлять их не по порядку. Я не думаю, что есть эффективный способ решить эту проблему.
А как же operator --
? Хотя в библиотеке С++ есть концепция односторонних итераторов. возможно, итераторы - лучшая метафора?
Конечно, как только вы начнете буферизовать и предоставлять итераторы, вы просто заново изобрели std::fstream
;)
@LightnessRacesinOrbit, ты fstream.
@LightnessRacesinOrbit Верно, хотя fstream обычно не использует итераторы таким же образом.
@GemTaylor Конечно, может!
Я не особо знаком с fstream
, но его использование немного отличается от того, что я имел в виду. Во всяком случае, то, что я собираюсь сделать, это в значительной степени (крайне неэффективная) эмуляция mmap
.
К вашему сведению, в идеале ваш
FilePointer operator++ ()
должен возвращать ссылку на*this
, а не копию.FilePointer& operator++ ()
. Так же и сoperator +=
. Есть очень хорошо сделанный Q / A на stackoverflow. Стоит прочитать.