Valgrind 认为我的 std::ranges 原始指针正在泄漏,即使它们已被复制到唯一指针之后

Valgrind thinks my std::ranges of raw pointers are leaking, even after they've been copied to unique pointers

我正在尝试使用 C++20 范围和函数式编程将所有 true-type 字体加载到一个目录中。然而,由于字体是一种资源,我在范围接口内分配内存。我认为这就是为什么 valgrind 认为我有泄漏。我有一些 std::views 新分配的原始指针,它们最终会被丢弃 - 然而,这些原始指针 转换并复制到一个唯一指针的向量中。

有问题的代码:

// free a font resource
struct font_deleter {
    void operator()(TTF_Font * font) { TTF_CloseFont(font); }
};

// aliases
using unique_font = std::unique_ptr<TTF_Font, font_deleter>;
using font_table = std::unordered_map<std::string, TTF_Font *>;

template<typename expected_t>
using result = tl::expected<expected_t, std::string>;

// determine if a path is a valid font file
auto _is_font_fxn(std::error_code & ec) {
    return [&ec](fs::path const & path) {
        return fs::is_regular_file(path, ec) and path.extension() == ".ttf";
    };
}

// load a font-name, font-pointer pair from a font file
font_table::value_type _load_as_pair(fs::path const & path)
{
    return std::make_pair(path.stem(), TTF_OpenFont(path.c_str(), 100));
}

// create a unique font pointer from a name-pointer pair
unique_font _ufont_from_pair(font_table::value_type const & pair)
{
    return unique_font(pair.second, font_deleter{});
}

// determine if a font pointer is null from a name-pointer pair
bool _font_is_null(font_table::value_type const & pair)
{
    return pair.second == nullptr;
}

result<std::vector<unique_font>>
button_factory::load_all_fonts(fs::path const & dir)
{
    namespace views = std::views;
    namespace ranges = std::ranges;

    std::error_code ec; // using error codes tells filesystem not to throw

    // make sure the path exists and is a valid directory
    if (not fs::exists(dir, ec) or not fs::is_directory(dir, ec)) {
        std::stringstream message;
        if (ec) { message << ec.message(); } // an os call failed
        else if (not fs::exists(dir)) { message << dir << " doesn't exist"; }
        else if (not fs::is_directory(dir)) { message << dir << " isn't a directory"; }
        return tl::unexpected(message.str());
    }

    // recursively get all paths in directory
    std::vector<fs::path> paths(fs::recursive_directory_iterator(dir, ec), {});

    // filter only font files and load them as name-font pairs
    auto is_font = _is_font_fxn(ec);
    auto fonts = paths | views::filter(is_font) | views::transform(&_load_as_pair);

    // put all the successfully loaded fonts into a vector of unique pointers
    // the font resources are freed automatically if returning unexpected
    // otherwise this is the expected result
    std::vector<unique_font> font_handle;
    auto into_handles = std::back_inserter(font_handle);
    ranges::transform(fonts, into_handles, &_ufont_from_pair);

    // abort if any os calls failed in the process, or if some fonts didn't load
    if (ec) { return tl::unexpected(ec.message()); }
    if (ranges::any_of(fonts, &_font_is_null)) { return tl::unexpected(TTF_GetError()); }

    // add all the loaded fonts definitions to the button factory
    auto into_table = std::inserter(_fonts, _fonts.end());
    ranges::copy(fonts, into_table);

    return font_handle;
}

附带说明一下,我使用的是 TartanLLama's implementation of the proposed std::expected,这是 tl:: 命名空间的来源。

我从 valgrind 收到了一些粗糙的模板错误消息,所以我将尝试只分享重要的部分。两个块中丢失了 62,000 个字节,虽然模板错误是一场噩梦,但它似乎全部来自范围接口。似乎第一个块中的消息来自对 ranges::any_of 的调用,第二个块中的消息来自 ranges::copy - 两个块都提到 _load_pair - 我猜是内存的来源那是在泄漏。

有什么原因我没有看到这可能会泄漏内存,还是这是一个 valgrind 错误?

views::transformlazy - 在访问视图元素之前不会调用转换函数。但这也意味着每次 访问视图元素时 都会调用变换函数。

所以每次你遍历 fonts - 首先是 transform,然后是 any_of,最后是 copy,你正在调用 _load_as_pair,因此再次 TTF_OpenFont - 并泄漏结果。