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::transform
是 lazy - 在访问视图元素之前不会调用转换函数。但这也意味着每次 访问视图元素时 都会调用变换函数。
所以每次你遍历 fonts
- 首先是 transform
,然后是 any_of
,最后是 copy
,你正在调用 _load_as_pair
,因此再次 TTF_OpenFont
- 并泄漏结果。
我正在尝试使用 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::transform
是 lazy - 在访问视图元素之前不会调用转换函数。但这也意味着每次 访问视图元素时 都会调用变换函数。
所以每次你遍历 fonts
- 首先是 transform
,然后是 any_of
,最后是 copy
,你正在调用 _load_as_pair
,因此再次 TTF_OpenFont
- 并泄漏结果。