我的函数采用文件系统路径和位掩码,描述要对 FS 对象执行的所需检查。
它存在吗? 是:文件、文件夹、符号链接?空的,隐藏的?可读(R),可写? 符号链接:到文件,到文件夹?
这 11 个标志提供了大量的组合,所以我提供方便的标志,如
SymLinkToAReadableFile
等。这不仅方便,还避免了一些潜在的混淆:SymLinkToFile | R
可能意味着可读的符号链接(不是很有用),名称“SymLinkToAReadableFile
”毫无疑问。
为了减少所需的处理,我会定义
RWHiddenFolder = Folder | R | W | Hidden
,所有与Folder
相关的东西都是一样的:Folder | ..
,同样适用于File
和SymLink
。
这使得检查用户没有混合
File
和Folder
(这没有意义)很容易:(flag & Folder) && (flag & File)
->错误。
但这使得
RWHiddenFolder | RWEmptyFile
与 RWHiddenFile | RWEmptyFolder
与 File | Folder | R | W | Empty | Hidden
相同:
SymLink
存在的情况下忽略Folder
,则无法判断用户是否想要Empty
或Hidden
(或两者)Folder
!对每个可能的组合产生一点影响的天真的“解决方案”显然解决了所有这些问题,但是已经建立了一个密集的
if/else
分支森林,只是为了确保提供的标志组合有意义,更不用说在之后进行实际检查了那..
在复杂的处理和方便/清晰的界面(对于用户)之间是否存在良好的权衡?
我想出了一个合理的解决方案(我不得不!)。 在这里分享给任何有同样问题的人。
对于那些喜欢行动而不是讲课的人(比如我):sample(忽略众多常量定义)
显然
enum
无法做到这一点,简单的位域/集合也无法做到这一点:与 OR
组合失去了标志的身份(ReadableFile == Readable | File
)。
但是二元运算使得操作非常有效(阅读“紧凑”)(检查,提取标志,..)
所以想法是携带
mask
并分开id
:
constexpr static auto nFlags = 96u; // total amount of flags
struct argFlag {
string n; // reflection
bitset<nFlags> id;
bitset<16> mask; // plain bitmask
// id == position of true bit
argFlag(const string& s, const int i, const uint32_t m = 0 )
: n(s), mask(m) { id.set(i); }
// Cumulate id & mask bits, preserve reflection
argFlag operator| (const argFlag& b)
{ argFlag r{ (n.empty()? ""s : n+" | ") + b.n, 0 };
r.id = id | b.id; r.mask = mask | b.mask; return /* move */r; }
// operator<< (has atom Id ? Meaning flag = .. | Atom | .. )
bool operator<< (const argFlag& b)
{ .. }
}
现在您可以制作提供给用户的基本标志列表,这些是“原子”标志:每个标志在
true
中都有1个id
位。
现在当用户通过标志
Symlink2Folder | HiddenFile
:
id
Symlink2HiddenFolder | File
混淆(相同的 mask
,但不同的 id
位):可以实现准确的错误消息。mask
成员设置了所有正确的位 (Symlink | Folder | Hidden | File
)。用于廉价的逻辑检查。到达你需要的任何成员来实现对标志的操作(检查 id,检查逻辑组成)。
使用几个 lambda 和宏,
validateFlags()
(example)可以用不到 100 行代码非常可读地编写来处理无数难以理解的组合(4 个原子构成 96x95x94x93 ~ 8000 万种组合)。
您可以尝试使用您能想出的任何不太可能的组合在
validateFlags()
中通过main()
!祝你好运;-)