11

I am wondering if there is a better way to write this for better readability. If you have a function like below,

void animal(bool hasFourLegs, bool hasHead, bool hasBody);

When you call the function, you will end up with something like

animal(true, false, true);

and this makes me go take a look at the definition every time I encounter function like this.

SO...

I do something like this!

const bool HAS_FOURLEGS = true;
const bool NO_HEAD = false;
const bool HAS_BODY = true;

animal(HAS_FOURLEGS, NO_HEAD, HAS_BODY);

But I do not like to declare const bool every time I call the function.

It seems like CPP does not support something like

animal(bool hasFourlegs = true, bool hasHead = false, bool hasBody = true);

Is there any better and shorter way?

Eddie
  • 761
  • 3
  • 9
  • 22
  • As a rule of thumb, do not declare variables in all capital letter style. It's usually intended for macros – Paolo M Sep 23 '15 at 15:52
  • 1
    this is why it is important to have a good IDE. with just a hover you can see the function signature – David Haim Sep 23 '15 at 15:52
  • the signature of your function is exactly what your IDE is supposed to be telling you - don't use plain text editors – BeyelerStudios Sep 23 '15 at 15:52
  • I use vim with YouCompleteMe. When I do coding, it works pretty good. But when I am reading, oh man.. lol – Eddie Sep 23 '15 at 15:53
  • You could consider declaring an `enum` and using it as a bitmask : `enum animaltype { HAS_FOURLEGS = 1, HAS_HEAD = 2, HAS_BODY = 4}`, and then just call `animal(HAS_FOURLEGS | HAS_BODY)`. This feels much more C than C++, though. – Daniel Kamil Kozar Sep 23 '15 at 15:54
  • 1
    Why is there no animal class ? Of course the problem would then maybe move to the constructor of that class. But the enum solution is a perfect fit for that. – Philip Stuyck Sep 23 '15 at 15:59

9 Answers9

16

When I run into issues related to this I sometimes create an enum even when there are only 2 expected choices:

For example, instead of the following function declaration:

bool search(..., bool recursive);

I'd go with:

enum class SearchOpt
{
    Recursive,
    NonRecursive
};

bool search(..., SearchOpt opt);

Therefore, the calling syntax changes from:

bool found = search(..., true);

to:

bool found = search(..., SearchOpt::Recursive);

Note: this avoids you having to create your own constants every time you call the function.

Edit

As others have suggested, instead of having separate bools for each option and thereby a separate enum for each it would make sense to have a single enum configured as bit flags.

SCFrench
  • 8,244
  • 2
  • 31
  • 61
James Adkison
  • 9,412
  • 2
  • 29
  • 43
  • Something that's implied here is that you then need to code `if (opt == SearchOpt.Recursive)` rather than `if (recursive)` inside the `search` function. The alternative, as you've used an enum class, is to use `if (static_cast(opt))`, which is untidy. It would be nice to be able to declare and implement something like `bool operator bool(SearchOpt value) { return value == SearchOpt::Recursive) }` which would be better in the `search` function, but slightly nasty otherwise (IMO :-) ), however `operator bool` can't be overridden in this way :-( ) – cosimo193 Mar 29 '23 at 09:32
10

Use flags:

  enum {
    HAS_LEGS = 0x01,
    HAS_HEAD = 0x02,
    HAS_BODY = 0x04,
  };

  void animal(int properties);

  animal(HAS_LEGS | HAS_HEAD);
Kamajii
  • 1,826
  • 9
  • 21
  • Then use type-safe, introspection-aware flags like `QFlags` from the Qt library. Or overload some operator such as `operator<<` to create a class factory. – Kamajii Sep 23 '15 at 16:37
  • 1
    @SergeyA: In the sense that they are soo fast, short time to code, less lines of code while keeping code readable, keep interfaces stable over time? ;-p – cfi Sep 23 '15 at 20:05
  • @cfi, not really. In the sense that they are polluting namespaces (HAS_LEGS only has meaning for animal(), but will be visible for everybody else (and yes, I know about class enums), they are painful to maintain, they are cryptic when looking at call stack... Enough? – SergeyA Sep 23 '15 at 20:14
3

One other option is to use a class to hold the parameters where they're closely related:

struct AnimalOptions {
  bool hasHead, hasBody, hasLegs;
  AnimalOptions() : hasHead(false), hasBody(false), hasLegs(false);
}

...

AnimalOptions opt;
  opt.hasHead = true;

  animal(opt);

This technique is useful whenever you have a function which seems to take a bunch of parameters with identical types, whose order isn't easily remembered. It's just as useful when your function take several ints.

Roddy
  • 66,617
  • 42
  • 165
  • 277
  • With a bit more boilerplate, one can chain the modifications: `AnimalOptions().hasHead(false).hasBody(true)` – dyp Sep 23 '15 at 18:09
2

As a alternative to the other answers, I liked tagged_bool that Andrzej Krzemieński came up with on his blog.

Shane Powell
  • 13,698
  • 2
  • 49
  • 61
1

Strange no one suggested named parameters from Boost.parameter: http://www.boost.org/doc/libs/1_59_0/libs/parameter/doc/html/index.html

SergeyA
  • 61,605
  • 5
  • 78
  • 137
0

Comments are your friends!

animal(   true,   //hasFourLegs
          false,  //hasHead
          true    //hasBody
      );
SlySherZ
  • 1,631
  • 1
  • 16
  • 25
  • 3
    comments like this cannot make up for bad code. Don't write comment write readable code. – Philip Stuyck Sep 23 '15 at 15:57
  • 1
    This would make it to leaderboard of my 'Worst Comment Ever' competition. While the first place is fixed for "++i; // Increment i" comments and the second is permanently occupied by class A { A(); // Constructor this is also notable example. – SergeyA Sep 23 '15 at 16:16
  • This is in the same category as /*ref*/ for parameters passed as mutable &. It's the reason that C# introduced keyword "ref" so that callers would have to be annotated. The problem on the boolean parameters is the same issue -- we want the language to force the call site to be annotated. To me, this seems like an IDE issue, not a language issue. – srm Jul 13 '17 at 15:47
0

You could use bitwise values, as follows:

const int hasLegs = 0x01;
const int noHead = 0x02;
const int hasBody = 0x04;

Then call animal with any combination of the above, e.g.: animal(hasLegs + hasBody);

Decalre animal with a single int parameter.

inside `animal`, test the bits:
if (parameter & haasBody)
{
    // it has a body....
}
Logicrat
  • 4,438
  • 16
  • 22
0

C++20 has designated initializers as part of aggregate initialization. You could make a struct with the boolean parameters and pass the struct by value. You can even have default parameter values.

struct AnimalParts {
    bool hasFourLegs = false;
    bool hasHead = true;
    bool hasBody = true;
}

void animal(AnimalParts parts);

Then use it like this:

animal({.hasFourLegs = true, .hasHead = false});

This comes very close to the named parameters idiom you suggested. In terms of compilation both options seem to produce comparable output, see on Godbolt.

Nicolas
  • 6,611
  • 3
  • 29
  • 73
0

I'm not sure it's a correct way to go, but still I cannot resist sharing this thought.

Let's imagine the function is not yours, but rather from some popular API which is hard to change.

void animal(bool hasFourLegs, bool hasHead, bool hasBody);

In this case it's possible to call it like this:

animal(bool("hasFourlegs"), !bool("hasHead"), bool("hasBody"));

The C-string is always a non zero pointer, which is converted to true.

One possible downside is compilation time...? Another is increase in length of code rows...

Andrei
  • 750
  • 8
  • 9