我正在尝试围绕c头dirent.h编写一个现代的c ++包装器。
要读取C中目录的内容,请编写如下内容:
int listdir(const char *path) {
struct dirent *entry;
DIR *dp;
dp = opendir(path);
if (dp == NULL) {
perror("opendir");
return -1;
}
while((entry = readdir(dp)))
puts(entry->d_name);
closedir(dp);
return 0;
}
将此转换为现代c ++,我有以下内容(其中m_dir和m_dirent为std::unique_ptr
,m_files为std::vector<string>
)
filesystem::Directory::Directory(std::string dir) : m_dir(opendir(dir.c_str()), closedir),
m_dirent(new struct dirent())
{
//If the directory can not be read, throw an error.
if (!m_dir) {
sdl2::SDLFileSystemRead_Failure ex;
throw ex;
}
while (&(*m_dirent = *readdir(&*m_dir)))
{
m_files.emplace_back(std::string(m_dirent->d_name));
}
}
这只有一半有效。当我写这篇文章时,我忘记了我只是检查表达式*m_dirent = *readdir(&*m_dir)
的地址是否存在(当然,它确实存在!)。
根据{{3}},readdir(DIR *)如果已读取最后一个文件并且没有更多文件需要读取,则返回空指针。但是,我不确定如何在.reset()
上调用m_dirent
的情况下将dirent指针设置为dir指针正在读取的内容。但是,这样做只会导致读取垃圾数据,因为我认为在dirent
被销毁时文件指针会丢失。
我该如何转换
while((entry = readdir(dp)))
puts(entry->d_name);
进入现代C ++?
答案 0 :(得分:2)
我不确定这个算法是否像现代一样,因为我在上一个千年中在UseNet中发布了这样的内容(here是精致版本)。它在1998年成为第一批用于播种Boost的组件之一。它由于其他人的工作而在Boost得到了进一步的发展并最终变成file system library,形成了File System TS的基础。 {{3}}。
但是,这一切都始于一个简单的想法:如何很好地公开opendir()
,readdir()
和closedir()
?有点明显的答案是:使用迭代器!这是一个简单的版本和演示:
#include <iostream>
#include <iterator>
#include <algorithm>
#include <string>
#include <stdexcept>
#include <memory>
#include <dirent.h>
class dir_it
: public std::iterator<std::input_iterator_tag, std::string>
{
std::shared_ptr<DIR> dir;
std::string current;
void advance() {
dirent entry;
dirent* result;
if (!readdir_r(dir.get(), &entry, &result) && result) {
this->current = result->d_name;
}
else {
this->dir.reset();
}
}
public:
dir_it(std::string const& path)
: dir(opendir(path.c_str()), [](DIR* dir){ dir && closedir(dir); }) {
if (!dir) {
throw std::runtime_error("failed to open directory '" + path + "'");
}
this->advance();
}
dir_it(): dir() {}
std::string const& operator*() const { return this->current; }
dir_it& operator++() { this->advance(); return *this; }
dir_it operator++(int) {
dir_it rc(*this);
this->operator++();
return rc;
}
bool operator==(dir_it const& other) const {
return bool(this->dir) == bool(other.dir);
}
bool operator!=(dir_it const& other) const {
return !(*this == other);
}
};
int main() {
std::copy(dir_it("."), dir_it(), std::ostream_iterator<std::string>(std::cout, "\n"));
}
当然,Boost和文件系统TS中的文件系统库比这种有点天真的实现更强大 。如果您的实现附带了TS的实现,我会使用它。如果不是,您可能需要考虑Boost的实施。
答案 1 :(得分:1)
逗号操作员救援!
{{1}}
尽管如评论所说,这对于文件系统来说并不是一个很棒的API。例如,透明地调整基础目录的迭代器很酷。