Is there a recommended "safe" way to specify a bit-chord made out of constant flag-values?

does natural sugar count towards daily intake
what is added sugar
recommended grams of sugar per day for diabetics
how many grams of natural sugar per day
recommended sugar intake
how many grams of sugar per day to lose weight for a woman
daily sugar intake calculator
how much sugar does the average american consume per day

The situation: occasionally I write a function that can take a number of boolean parameters, and instead of writing something like this:

void MyFunc(bool useFoo, bool useBar, bool useBaz, bool useBlah);

[...]

// hard to tell what this means (requires looking at the .h file)
// not obvious if/when I got the argument-ordering wrong!
MyFunc(true, true, false, true);

I like to be able to specify them using a bit-chord of defined bits-indices, like this:

enum {
   MYARG_USE_FOO = 0,
   MYARG_USE_BAR,
   MYARG_USE_BAZ,
   MYARG_USE_BLAH,
   NUM_MYARGS
};

void MyFunc(unsigned int myArgsBitChord);

[...]

// easy to see what this means
// "argument" ordering doesn't matter
MyFunc((1<<MYARG_USE_FOO)|(1<<MYARG_USE_BAR)|(1<<MYARG_USE_BLAH));

That works fine, in that it allows me to pass around a lot of boolean arguments easily (as a single unsigned long, rather than a long list of separate bools), and I can easy see what my call to MyFunc() is specifying (without having to refer back to a separate header file).

It also allows me to iterate over the defined bits if I want to, which is sometimes useful:

unsigned int allDefinedBits = 0;
for (int i=0; i<NUM_MYARGS; i++) allDefinedBits |= (1<<i);

The main downsides are that it can be a bit error-prone. In particular, it's easy to mess up and do this by mistake:

// This will compile but do the wrong thing at run-time!
void MyFunc(MYARG_USE_FOO | MYARG_USE_BAR | MYARG_USE_BLAH);

... or even to make this classic forehead-slapping mistake:

// This will compile but do the wrong thing at run-time!
void MyFunc((1<<MYARG_USE_FOO) | (1<<MYARG_USE_BAR) || (1<<MYARG_USE_BLAH));

My question is, is there a recommended "safer" way to do this? i.e. one where I can still easily pass multiple defined booleans as a bit-chord in a single argument, and can iterate over the defined bit-values, but where "dumb mistakes" like the ones shown above will be caught by the compiler rather than causing unexpected behavior at runtime?

How about a template...

#include <iostream>

template <typename T>
constexpr unsigned int chordify(const T& v) {
    return (1 << v);
}

template <typename T1, typename... Ts>
constexpr unsigned int chordify(const T1& v1, const Ts&... rest) {
    return (1 << v1) | chordify(rest... );
}

enum {
   MYARG_USE_FOO = 0,
   MYARG_USE_BAR,
   MYARG_USE_BAZ,
   MYARG_USE_BLAH,
   NUM_MYARGS
};

int main() {
    static_assert(__builtin_constant_p(
        chordify(MYARG_USE_FOO, MYARG_USE_BAZ, MYARG_USE_BLAH)
    ));
    std::cout << chordify(MYARG_USE_FOO, MYARG_USE_BAZ, MYARG_USE_BLAH);
}

That outputs 13, and it's a compile-time constant.

Daily Intake of Sugar, can eat sugar in moderation, while others recommend avoiding it completely. Added sugar is the single worst ingredient in the modern diet. Admit, for so is the fact, that this plan is only RECOMMENDED, not imposed, yet let it be remembered that it is neither recommended to BLIND approbation, nor to BLIND reprobation; but to that sedate and candid consideration which the magnitude and importance of the subject demand, and which it certainly ought to receive.

#include <iostream>
#include <type_traits>
#include <cstdint>

enum class my_options_t : std::uint32_t {
    foo,
    bar,
    baz,
    end
};

using my_options_value_t = std::underlying_type<my_options_t>::type;

inline constexpr auto operator|(my_options_t const & lhs, my_options_t const & rhs)
{
    return (1 << static_cast<my_options_value_t>(lhs)) | (1 << static_cast<my_options_value_t>(rhs));
}

inline constexpr auto operator|(my_options_value_t const & lhs, my_options_t const & rhs)
{
    return lhs | (1 << static_cast<my_options_value_t>(rhs));
}

inline constexpr auto operator&(my_options_value_t const & lhs, my_options_t const & rhs)
{
    return lhs & (1 << static_cast<my_options_value_t>(rhs));
}

void MyFunc(my_options_value_t options)
{
    if (options & my_options_t::bar)
        std::cout << "yay!\n\n";
}

int main()
{
    MyFunc(my_options_t::foo | my_options_t::bar | my_options_t::baz);
}

Recommended sugar intake: How much should you have per day?, The average person in the United States consumes around 17 teaspoons, or 71.14 grams, of added sugar per day, which far exceeds  U.S. News uses these qualities to rank the 100 Best Jobs of 2020. Software developer, dentist, physician assistant, orthodontist and nurse practitioner are among the top-ranked careers on the list.

You can use a bit field, which allows you to efficiently construct a structure with individually-named bit flags.

for example, you could pass a struct myOptions to the function, where it is defined as:

struct myOptions {
  unsigned char foo:1;
  unsigned char bar:1;
  unsigned char baz:1;
};

Then, when you have to construct the values to send to the function, you'd do something like this:

myOptions opt;
opt.foo = 1;
opt.bar = 0;
opt.baz = 1;
MyFunct(opt);

Bit fields are compact and efficient, yet allow you to name the bits (or groups of bits) as if they were independent variables.

By the way, given the verbosity of the declaration, this is one place where I might break the common style of only declaring one variable per statement, and declare the struct as follows:

struct myOptions {
  unsigned char foo:1, bar:1, baz:1;
};

And, in C++20 you can add initializers:

struct myOptions {
  unsigned char foo:1{0}, bar:1{0}, baz:1{0};
}

How Much Is Too Much?, Expert panels worldwide have made consistent recommendations on daily sugar intake. The American Heart Association (AHA) recommends no more than 6  The truth is that there's no reliable evidence to suggest that calories are burned more efficiently at certain times of day. But the time of day can influence how you feel when exercising. The most important thing, experts say, is to choose a time of day you can stick with, so that exercise becomes a habit.

Recommended Sugar Intake: What It Actually Looks Like, The average American consumes about 17 teaspoons of added sugar per day. But government dietary guidelines recommend limiting added sugar to no more  With many tablet buyers opting for Apple iPads, the Android tablet market isn't in the best place.Fewer and fewer manufacturers even make them now. But that doesn't mean there aren't a few good

Added Sugar in the Diet | The Nutrition Source, A good rule of thumb is to avoid products that have a lot of added sugar, including skipping foods that list “sugar” as the first or second ingredient. However, the  When it comes to wireless speakers, there are plenty of options -- perhaps too many. Here's a look at some of our current favorites for the best portable Bluetooth speakers, from tiny micro models

How much protein do you need every day?, The Recommended Dietary Allowance (RDA) for protein is a modest 0.8 Research on how much protein is the optimal amount to eat for good  Take the There, They're, Their Quiz Now! Use this quiz to practice using the right form of there, they're, or their. Decide which word correctly fills in the blank below and click "Get Answer" to see if you are right. To get a new question, just click here (or refresh the page). _____ is a store that sells those.

Comments
  • Usually the enum values are defined directly as powers of 2.
  • MyFunc((1<<MYARG_USE_FOO)|(1<<MYARG_USE_BAR)|(1<<MYARG_USE_BLAH)); looks horrible to me. Why wouldn't you just assign different bit values to the enum values and avoid all the terrible shifts when you need to call the function. enum { BIT_1 = 1<<0;, BIT_2 = 1<<1, bit_3=1<<2, etc...
  • @RetiredNinja: because if I do that, then I can't iterate over the bits' values anymore (or at least, not easily)
  • I guess if I had to decide between something that looked terrible and was hard to write everywhere and something that is "sometimes useful", I'd eliminate the terrible.
  • Best to craft the interface to how the client will use it.