Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wrap several boolean flags into struct to pass them to a function with a convenient syntax

In some testing code there's a helper function like this:

auto make_condiment(bool salt, bool pepper, bool oil, bool garlic) {
    // assumes that first bool is salt, second is pepper,
    // and so on...
    //
    // Make up something according to flags
    return something;
};

which essentially builds up something based on some boolean flags.

What concerns me is that the meaning of each bool is hardcoded in the name of the parameters, which is bad because at the call site it's hard to remember which parameter means what (yeah, the IDE can likely eliminate the problem entirely by showing those names when tab completing, but still...):

// at the call site:
auto obj = make_condiment(false, false, true, true); // what ingredients am I using and what not?

Therefore, I'd like to pass a single object describing the settings. Furthermore, just aggregating them in an object, e.g. std::array<bool,4>.

I would like, instead, to enable a syntax like this:

auto obj = make_smart_condiment(oil + garlic);

which would generate the same obj as the previous call to make_condiment.

This new function would be:

auto make_smart_condiment(Ingredients ingredients) {
    // retrieve the individual flags from the input
    bool salt = ingredients.hasSalt();
    bool pepper = ingredients.hasPepper();
    bool oil = ingredients.hasOil();
    bool garlic = ingredients.hasGarlic();
    // same body as make_condiment, or simply:
    return make_condiment(salt, pepper, oil, garlic);
}

Here's my attempt:

struct Ingredients {
  public:
    enum class INGREDIENTS { Salt = 1, Pepper = 2, Oil = 4, Garlic = 8 };
    explicit Ingredients() : flags{0} {};
    explicit Ingredients(INGREDIENTS const& f) : flags{static_cast<int>(f)} {};
  private:
    explicit Ingredients(int fs) : flags{fs} {}
    int flags; // values 0-15
  public:
    bool hasSalt() const {
        return flags % 2;
    }
    bool hasPepper() const {
        return (flags / 2) % 2;
    }
    bool hasOil() const {
        return (flags / 4) % 2;
    }
    bool hasGarlic() const {
        return (flags / 8) % 2;
    }
    Ingredients operator+(Ingredients const& f) {
        return Ingredients(flags + f.flags);
    }
}
salt{Ingredients::INGREDIENTS::Salt},
pepper{Ingredients::INGREDIENTS::Pepper},
oil{Ingredients::INGREDIENTS::Oil},
garlic{Ingredients::INGREDIENTS::Garlic};

However, I have the feeling that I am reinventing the wheel.

  • Is there any better, or standard, way of accomplishing the above?

  • Is there maybe a design pattern that I could/should use?

like image 452
Enlico Avatar asked Jan 19 '26 21:01

Enlico


1 Answers

I think you can remove some of the boilerplate by using a std::bitset. Here is what I came up with:

#include <bitset>
#include <cstdint>
#include <iostream>

class Ingredients {
public:
    enum Option : uint8_t {
        Salt = 0,
        Pepper = 1,
        Oil = 2,
        Max = 3
    };

    bool has(Option o) const { return value_[o]; }

    Ingredients(std::initializer_list<Option> opts) {
        for (const Option& opt : opts)
            value_.set(opt);
    }

private:
    std::bitset<Max> value_ {0};
};

int main() {
    Ingredients ingredients{Ingredients::Salt, Ingredients::Pepper};
    
    // prints "10"
    std::cout << ingredients.has(Ingredients::Salt)
              << ingredients.has(Ingredients::Oil) << "\n";
}

You don't get the + type syntax, but it's pretty close. It's unfortunate that you have to keep an Option::Max, but not too bad. Also I decided to not use an enum class so that it can be accessed as Ingredients::Salt and implicitly converted to an int. You could explicitly access and cast if you wanted to use enum class.

like image 148
mattlangford Avatar answered Jan 21 '26 10:01

mattlangford