Разделение имени файла и асболютного пути на уровне типов в C++
Я разрабатываю API класса на C++, который загружает данные из некоторых файлов.
При первой загрузке данных из файла, я передаю полный путь до файла, а в дальнейшем обращаюсь к данным только по имени файла, а не по абсолютному пути.
Для обоих типов операций я использую std::string.
И вроде бы в целом всё работает, но возникает проблема: перепутать относительный и абсолютный путь довольно легко, когда всё передаётся как строка, и в рантайме это может вызвать ошибки, не говоря уже о том что такие проблемы не отловятся в compile time с constexpr строками.
Очевидное решение:
Добавить какой-нибудь метод по типу isAbsolutePath, и вызывать его в начале каждого метода, который ожидает имя файла, и кидать исключение если пользователь накосячил.
Но в таком случае придётся вручную прописывать это в начале каждой функции, что очень неудобно, да и писать велосипеды не хочется, + программа кроссплатформенная, из-за чего придётся учитывать всякие слеши в разные стороны и прочее.
Чего я ищу: Вопрос в том, можно ли какими-то стандартными методами, из STL или из, скажем, буста, разделять имя файла и абсолютный путь на уровне типов?
В std::filesystem есть только тип fs::path, который, к сожалению, может хранить и просто имя файла и абсолютный путь, да и к тому же может неявно конвертироваться из строки, что мне тоже не очень нравится.
Под конец: задача то в целом примитивная, и думаю лет 20 назад её тоже кто-то решал, поэтому задаю вопрос, чтобы узнать, сталкивался ли кто-то с таким, и как бы вы такой вопрос решали стандартными средствами и без велосипедов, если такое возможно. Спасибо.
Ответы (1 шт):
Чего я ищу: Вопрос в том, можно ли какими-то стандартными методами, из STL или из, скажем, буста, разделять имя файла и абсолютный путь на уровне типов?
Если так:
#include <filesystem>
namespace fs = std::filesystem;
struct apath : fs::path {};
struct rpath : fs::path {};
namespace std::filesystem {
apath absolute(const rpath& rp) {
return apath(fs::absolute(fs::path(rp)));
}
void absolute(const apath& ap) { }
}
void foo(const apath &ap, const rpath& rp) {}
int main() {
fs::path p = "foo.c";
// rpath rp = p; // Запрет
rpath rp{p};
// rpath rp1 = "xyz"; // Запрет, TODO для rpath, возможно, и нужна
rpath rp1{"xyz"};
apath ap = fs::absolute(rp);
// apath ap1 = rp; // Запрет
// apath ap1{fs::absolute(ap)}; // Запрет
apath ap1{"xyz"};
foo(ap, rp);
// foo(rp, ap); // Запрет
foo(apath(rp), rpath(ap));
// foo("xyz", ap); // Запрет
foo(apath("xyz"), rpath("чня"));
ap1 = ap;
// ap1 = rp; // Запрет
rp1 = rp;
// rp1 = ap; // Запрет
p = fs::canonical(ap);
p = fs::weakly_canonical(rp);
}
Или Вы ищите уже готовый, продуманный и отлаженный инструмент из коробки?