Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement flags with the options true,false,default and toggle in C++?

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?

Edit for clarification: I don't want to pass the 4 distinct flags "true", "false", "default", "toggle" to a function.
E.g. think of a circle that gets drawn where the flags are used for "draw border", "draw center", "draw fill color", "blurry border", "let the circle hop up and down", "do whatever other fancy stuff you can do with a circle", ....
And for each of those "properties" i want to pass a flag with value either true, false, default or toggle. So the function might decide to draw the border, fill color and center by default, but none of the rest. A call, roughly like this:

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....

should draw the border (specified by flag), not draw the center (specified by flag), blurry the border (the flag says: not the default) and draw the fill color (not specified, but its default).
If i later decide to not draw the center by default anymore but blurry the border by default, the call should draw the border (specified by flag), not draw the center (specified by flag), not blurry the border (now blurrying is default, but we don't want default) and draw the fill color (no flag for it, but its default).

like image 553
Anedar Avatar asked Oct 31 '22 01:10

Anedar


1 Answers

Not exactly pretty, but very simple (building from your Approach 1):

#include <iostream>

using Flag = int;
static const Flag Flag1 = 1<<0;
static const Flag Flag2 = 1<<2;
// add more flags to turn things off, etc.

class Foo
{
    bool flag1 = true;      // default true
    bool flag2 = false;     // default false

    void applyDefault(Flag& f)
    {
        if (f & Flag1)
            flag1 = true;
        if (f & Flag2)
            flag2 = true;
        // apply off flags
    }

public:
    void operator()(/*args ,*/ Flag f)
    {
        applyDefault(f);
        if (flag1)
            std::cout << "Flag 1 ON\n";
        if (flag2)
            std::cout << "Flag 2 ON\n";
    }
};

void foo(/*args ,*/ Flag flags)
{
    Foo f;
    f(flags);
}

int main()
{
    foo(Flag1); // Flag1 ON
    foo(Flag2); // Flag1 ON\nFlag2 ON
    foo(Flag1 | Flag2); // Flag1 ON\nFlag2 ON
    return 0;
}
like image 106
Leben Asa Avatar answered Nov 02 '22 11:11

Leben Asa