I'm currently trying to come up with a clever way of implementing flags that include the states "default" and (optional) "toggle" in addition to the usual "true" and "false".
The general problem with flags is, that one has a function and wants to define its behaviour (either "do something" or "don't do something") by passing certain parameters.
Single flag
With a single (boolean) flag the solution is simple:
void foo(...,bool flag){
if(flag){/*do something*/}
}
Here it is especially easy to add a default, by just changing the function to
void foo(...,bool flag=true)
and call it without the flag parameter.
Multiple flags
Once the number of flags increases, the solution i usually see and use is something like this:
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)
This has the advantage, that you don't need a parameter for each flag, which means the user can set the flags he likes and just forget about the ones he don't care about. Especially you dont get a call like foo (/*args*/, true, false, true)
where you have to count which true/false denotes which flag.
The problem here is:
If you set a default argument, it is overwritten as soon as the user specifies any flag. It is not possible to do somethink like Flag1=true, Flag2=false, Flag3=default
.
Obviously, if we want to have 3 options (true, false, default) we need to pass at least 2 bits per flag. So while it might not be neccessary, i guess it should be easy for any implementation to use the 4th state to indicate a toggle (= !default).
I have 2 approaches to this, but i'm not really happy with both of them:
Approach 1: Defining 2 Flags
I tried using something like this up to now:
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
}
However what i don't like about this is, that there are two different bits used for each flag. This leads to the different code for "default-true" and "default-false" flags and to the neccessary if instead of some nice bitwise operation in applyDefault()
.
Approach 2: Templates
By defining a template-class like this:
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;
i was able to condense the apply
into a single bitwise operation, with all but 1 argument known at compile time. So using the TFlag::apply
directly compiles (using gcc) to the same machine code as a return true;
, return false;
, return prev;
or return !prev;
would, which is pretty efficient, but that would mean i have to use template-functions if i want to pass a TFlag
as argument. Inheriting from Flag
and using a const Flag&
as argument adds the overhead of a virtual function call, but saves me from using templates.
However i have no idea how to scale this up to multiple flags...
Question
So the question is: How can i implement multiple flags in a single argument in C++, so that a user can easily set them to "true", "false" or "default" (by not setting the specific flag) or (optional) indicate "whatever is not default"?
Is a class with two ints, using a similar bitwise operation like the template-approach with its own bitwise-operators the way to go? And if so, is there a way to give the compiler the option to do most of the bitwise operations at compile-time?
Aucun commentaire:
Enregistrer un commentaire