Разделение имени файла и асболютного пути на уровне типов в C++

Я разрабатываю API класса на C++, который загружает данные из некоторых файлов.

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

Для обоих типов операций я использую std::string.

И вроде бы в целом всё работает, но возникает проблема: перепутать относительный и абсолютный путь довольно легко, когда всё передаётся как строка, и в рантайме это может вызвать ошибки, не говоря уже о том что такие проблемы не отловятся в compile time с constexpr строками.

Очевидное решение: Добавить какой-нибудь метод по типу isAbsolutePath, и вызывать его в начале каждого метода, который ожидает имя файла, и кидать исключение если пользователь накосячил.

Но в таком случае придётся вручную прописывать это в начале каждой функции, что очень неудобно, да и писать велосипеды не хочется, + программа кроссплатформенная, из-за чего придётся учитывать всякие слеши в разные стороны и прочее.

Чего я ищу: Вопрос в том, можно ли какими-то стандартными методами, из STL или из, скажем, буста, разделять имя файла и абсолютный путь на уровне типов?

В std::filesystem есть только тип fs::path, который, к сожалению, может хранить и просто имя файла и абсолютный путь, да и к тому же может неявно конвертироваться из строки, что мне тоже не очень нравится.

Под конец: задача то в целом примитивная, и думаю лет 20 назад её тоже кто-то решал, поэтому задаю вопрос, чтобы узнать, сталкивался ли кто-то с таким, и как бы вы такой вопрос решали стандартными средствами и без велосипедов, если такое возможно. Спасибо.


Ответы (1 шт):

Автор решения: Serge3leo

Чего я ищу: Вопрос в том, можно ли какими-то стандартными методами, из 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);
}

Или Вы ищите уже готовый, продуманный и отлаженный инструмент из коробки?

→ Ссылка