我正处于编写 C++ 程序的早期阶段,该程序复制了 bash 命令的最基本功能ls
。
我使用<filesystem>
标题。
该库中的std::filesystem::directory_iterator
和std::filesystem::recursive_directory_iterator
分别帮助我以浅层或递归的方式遍历输入文件路径。
输入接收一个argv[1]
,它应该由一个首连字符-
和后面代表选项标志的字母组成。标志R
代表递归遍历所有子目录,否则,只查看给定的路径。
argv[2]
是要探索的文件路径。
然而我的问题是如何以模板化的方式运行它,或者是否完全可能。
作为测试,我正在使用std::variant
一个函数,该函数根据输入中是否存在标志来option_R()
选择正确的。directory_iterator
-R
由于这将是运行时多态性,我怀疑我是否可以实现类似下面的内容:
#include <iostream>
#include <vector>
#include <string>
#include <filesystem>
#include <variant>
#include <type_traits>
#include <concepts>
using namespace std;
using namespace std::filesystem;
template<typename T> concept isIter =
is_same<T, directory_iterator>::value || is_same<T, recursive_directory_iterator>::value;
template <isIter dirIterator>
dirIterator option_R(std::string_view options, const path &my_path) {
variant<directory_iterator, recursive_directory_iterator> my_iter;
if (options.find('R') != string::npos) {
my_iter.emplace<recursive_directory_iterator>(my_path, directory_options::skip_permission_denied);
return get<recursive_directory_iterator>(my_iter);
} else {
my_iter.emplace<directory_iterator>(my_path);
return get<directory_iterator>(my_iter);
}
}
template <isIter dirIterator>
void traverse(isIter &my_iter, vector<string> &my_vec) {
cout << "This function traverses the given dir path, "
<< "stores all filenames found inside a vector." << endl;
for (const auto &entry : my_iter)
my_vec.push_back(entry.path().string());
}
int main(int argc, const char **argv) {
const path sys_path{argv[2]};
const string options{argv[1]};
vector<string> files;
if (options[0] == '-') {
if (is_directory(sys_path)) {
isIter file_iterator = option_R(options, sys_path);
traverse(file_iterator, files);
}
cout << "after this the program will parse other options" << endl;
} else {
cout << "simply print all files" << endl;
}
}
上述内容无法编译,其中一个原因是因为我随意使用概念isIter
来option_R()
调用main
。
有没有办法编写option_R()
,以便它只做一件事:directory_iterator
根据是否-R
给出标志来选择正确的 - 然后将其传回main()
?
将其打包option_R()
并traverse()
放在一个函数中很容易,但是这会做多件事,并且代码会更难理解。
std::variant
您可以通过和实现您想要做的事情std::visit
。A
std::variant
是一个更安全的联合,您可以用它来存储 astd::filesystem::directory_iterator
和 astd::filesystem::recursive_directory_iterator
,而不必为两个迭代器分别为分配内存。然后,您
std::visit
就可以将其std::variant
传递给模板化(或重载)函数,该函数可以处理所有类型std::variant
。使用这个,我们可以让你的
option_R()
函数返回std::variant
上面提到的两个迭代器,然后traverse()
函数接受一个这样std::variant
的参数,然后使用它std::visit
以预期的方式迭代所有文件。(实例)
你非常接近了。问题是,你不能让你的
option_R()
函数返回在运行时确定的编译时模板类型。你需要让它返回std::variant
自身,然后调用者可以决定如何处理该值。你可以使用std::visit()
它来帮助你,例如: