C Dirent.h的现代C ++包装器

时间:2015-08-12 21:12:04

标签: c++ pointers c++11

我正在尝试围绕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 ++?

2 个答案:

答案 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。例如,透明地调整基础目录的迭代器很酷。