【发布时间】:2016-06-22 13:38:23
【问题描述】:
我目前正在尝试想出一种聪明的方法来实现标志,除了通常的“true”和“false”之外,还包括状态“default”和(可选)“toggle”。
标志的一般问题是,一个人有一个函数,并希望通过传递某些参数来定义它的行为(“做某事”或“不做某事”)。
单个标志
使用单个(布尔)标志,解决方案很简单:
void foo(...,bool flag){
if(flag){/*do something*/}
}
在这里添加默认值特别容易,只需将函数更改为
void foo(...,bool flag=true)
并在没有标志参数的情况下调用它。
多个标志
一旦标志的数量增加,我通常看到和使用的解决方案是这样的:
typedef int Flag;
static const Flag Flag1 = 1<<0;
static const Flag Flag2 = 1<<1;
static const Flag Flag3 = 1<<2;
void foo(/*other arguments ,*/ Flag f){
if(f & Flag1){/*do whatever Flag1 indicates*/}
/*check other flags*/
}
//call like this:
foo(/*args ,*/ Flag1 | Flag3)
这样做的好处是,您不需要为每个标志设置一个参数,这意味着用户可以设置他喜欢的标志,而忘记那些他不关心的标志。特别是你不会接到像foo (/*args*/, true, false, true) 这样的电话,你必须计算哪个真/假表示哪个标志。
这里的问题是:
如果您设置默认参数,则只要用户指定任何标志,它就会被覆盖。像Flag1=true, Flag2=false, Flag3=default 这样的想法是不可能的。
显然,如果我们想要有 3 个选项(真、假、默认),我们需要为每个标志传递至少 2 位。因此,虽然它可能不是必需的,但我想任何实现都应该很容易使用第 4 状态来指示切换(=!默认)。
我有两种方法,但我对这两种方法都不满意:
方法 1:定义 2 个标志
到目前为止,我尝试使用类似的东西:
typedef int Flag;
static const Flag Flag1 = 1<<0;
static const Flag Flag1False= 1<<1;
static const Flag Flag1Toggle = Flag1 | Flag1False;
static const Flag Flag2= 1<<2;
static const Flag Flag2False= 1<<3;
static const Flag Flag2Toggle = Flag2 | Flag2False;
void applyDefault(Flag& f){
//do nothing for flags with default false
//for flags with default true:
f = ( f & Flag1False)? f & ~Flag1 : f | Flag1;
//if the false bit is set, it is either false or toggle, anyway: clear the bit
//if its not set, its either true or default, anyway: set
}
void foo(/*args ,*/ Flag f){
applyDefault(f);
if (f & Flag1) //do whatever Flag1 indicates
}
但我不喜欢的是,每个标志都有两个不同的位。这会导致“default-true”和“default-false”标志的代码不同,并导致必要的 if 而不是 applyDefault() 中的一些不错的按位运算。
方法 2:模板
通过这样定义模板类:
struct Flag{
virtual bool apply(bool prev) const =0;
};
template<bool mTrue, bool mFalse>
struct TFlag: public Flag{
inline bool apply(bool prev) const{
return (!prev&&mTrue)||(prev&&!mFalse);
}
};
TFlag<true,false> fTrue;
TFlag<false,true> fFalse;
TFlag<false,false> fDefault;
TFlag<true,true> fToggle;
我能够将apply 压缩为一个按位运算,在编译时除了 1 个参数之外的所有参数都是已知的。所以使用TFlag::apply 直接编译(使用gcc)到与return true;、return false;、return prev; 或return !prev; 相同的机器代码,这非常有效,但这意味着我必须使用模板-functions 如果我想传递一个TFlag 作为参数。从Flag 继承并使用const Flag& 作为参数会增加虚函数调用的开销,但可以避免使用模板。
但是我不知道如何将其扩展到多个标志...
问题
所以问题是: 我如何在 C++ 中的单个参数中实现多个标志,以便用户可以轻松地将它们设置为“真”、“假”或“默认”(通过不设置特定标志)或(可选)指示“任何不是默认”?
一个有两个整数的类,使用类似的按位运算,如带有自己的按位运算符的模板方法,是要走的路吗?如果是这样,有没有办法让编译器选择在编译时执行大多数按位运算?
编辑澄清:
我不想将 4 个不同的标志“true”、“false”、“default”、“toggle”传递给函数。
例如。想一想绘制的圆圈,其中标志用于“绘制边框”、“绘制中心”、“绘制填充颜色”、“模糊边框”、“让圆圈上下跳跃”、“做任何其他花哨的事情你可以用一个圆圈来做”,....
对于这些“属性”中的每一个,我想传递一个值为真、假、默认或切换的标志。
因此,该函数可能会决定默认绘制边框、填充颜色和中心,但其余的都没有。一个调用,大致是这样的:
draw_circle (DRAW_BORDER | DONT_DRAW_CENTER | TOGGLE_BLURRY_BORDER) //or
draw_circle (BORDER=true, CENTER=false, BLURRY=toggle)
//or whatever nice syntax you come up with....
应该画边框(由flag指定),不画中心(由flag指定),模糊边界(flag说:不是默认的)并绘制填充颜色(没有指定,但是默认)。 如果我后来决定不再默认绘制中心但默认模糊边框,调用应该绘制边框(由标志指定),而不是绘制中心(由标志指定),不模糊边框(现在模糊是默认的,但我们不想要默认)并绘制填充颜色(没有标记,但它是默认的)。
【问题讨论】:
-
为什么不使用一种标志图呢?然后您需要正确合并地图(默认和参数)以获取标志...
-
与这些按位运算相比,映射不是效率极低吗?那么函数调用会是什么样子呢?
-
我说的是一种,不一定是 C++ 映射。无论如何,人们可以想象用
f(...,FlagMap(Flag1(true),Flag3(default),Flag17(toggle)));或类似的方式打电话吗?也许是一个标志集?... -
我不确定它是否适合我的情况(阅读:如果我可以教那些将使用它的人使用这个系统),但使用这样的“键值对”是一个有趣的想法" 符号。
-
std::vector<bool>呢?