title: Finding a type-safe replacement for int argument to a C++ function representing one or more operations
body: <h2>Question Summary</h2>
  
  <p>Is there a type-safe replacement of arguments to functions that take
  an <code>int</code> but that interpret the value as a set of bitwise values that
  represent mutually-exclusive operations, but that prevents callers
  from using magic numbers to represent those operations? This needs
  to work with GCC 4.8.5, without upgrading.</p>
  
  <h2>Detailed Question</h2>
  
  <p>I have inherited an old C-style C++ function that accepts an <code>int</code>
  argument that indicates some operations to do. These operations are
  currently passed to the function as <code>0x1 | 0x2</code>, and are spread out
  all over the code base. This is, in my opinion, a pretty
  unmaintainable situation: It requires callers to use magic numbers,
  and as such, forces the developers to read the entire function
  implementation just to understand the operations being requested.</p>
  
  <p>Thus, I seek to change the interface to use names that clearly
  indicate what is being requested of the function to perform, while
  still maintaining most of the existing function interface other than
  the change of the particular argument's type. I cannot, in the
  short-term, refactor the function to use a proper polymorphic approach
  where operations are represented as separate classes, as the use of
  these magic numbers has become too widespread do to that in one single
  refactoring operation. I desire to require that any additional
  operations added to the function then require some central type to be
  changed to clearly indicate all of the valid named combinations of
  operations that function must do, versus allowing impatient developers
  the luxury of hacking in yet another value, e.g., <code>0x6</code>, and changing
  the internals of the function to check for that value using ever
  increasing unmaintainable conditional logic. In my mockup below of
  where I've defined the <code>SomeEnum</code> type, and showed how it is still
  hackable (see <code>SCENARIO_4</code>). It is that hacking I wish to enlist the
  compiler to prevent.</p>
  
  <p>The best I have been able to come up with is the <code>SomeEnumImposter</code>
  class below. This is non-ideal from a caller point of view, since it
  is cumbersome to have to type expressions of the form:</p>
  
  <pre><code>SomeEnumImposterUsingFunction(SomeEnumImposter().C().D());
  </code></pre>
  
  <p>Ideally, I would be able to do this:</p>
  
  <pre><code>SomeEnumImposterUsingFunction(EIx(C, D));
  </code></pre>
  
  <p>where <code>EIx</code> would be some type of construct that would expand into
  that cumbersome expression above. But to do that, I'd have to resort
  to using variadic macros which are GCC-specific, or to avoid that, I'd
  have to manually expand out macros of the form:</p>
  
  <pre><code>#define EI1(x1) SomeEnumImposter().x1()
  #define EI2(x1, x2) SomeEnumImposter().x1().x2()
  #define EI3(x1, x2, x3) SomeEnumImposter().x1().x2().x3()
  #define EI4(x1, x2, x3, x4) SomeEnumImposter().x1().x2().x3().x4()
  // etc.
  </code></pre>
  
  <p>Frankly, this <code>SomeEnumImposter</code> class is quite a lot of code just to
  guard against impatient developers.  Is there a simpler way that works
  with this specific compiler (no compiler upgrades are allowed; see the
  gcc version being dumped out below).</p>
  
  <h2>Update #1</h2>
  
  <p>Added <code>CLASS_WITH_BOOL_DATAMEMBERS</code> as an attempted at using <code>struct Options</code> that was mentioned in the answer at <a href="https://stackoverflow.com/a/52309629/257924">https://stackoverflow.com/a/52309629/257924</a> </p>
  
  <p>That is getting closer, but the syntax is still RSI-producing, as it takes up at most three lines just to say "C or D" in the call:</p>
  
  <pre><code>Options options;
  options.C = true;
  options.D = true;
  SomeOptionsUsingFunction(options);
  </code></pre>
  
  <p>What I really need is something that is a mostly drop-in replacement for passing in hardcoded values into the existing function.</p>
  
  <h2>Update #2</h2>
  
  <p><a href="https://stackoverflow.com/a/52309629/257924">https://stackoverflow.com/a/52309629/257924</a> also mentions <code>myFunction</code> as a template function, but I cannot use that option since it means exposing the entire original function I am changing into a header, and it is too large to do that.</p>
  
  <h2>Mockup</h2>
  
  <p><code>main.cpp</code> contains:</p>
  
  <pre><code>#include &lt;stdio.h&gt;
  
  enum SomeEnum {
    E_INVALID = 0,
    E_A = 1,
    E_B = 1 &lt;&lt; 1,
    E_C = 1 &lt;&lt; 2,
    E_D = 1 &lt;&lt; 3,
  };
  
  void SomeEnumUsingFunction(SomeEnum se)
  {
    if (se &amp; (E_C | E_D)) {
      printf("Has: C or D\n");
    }
  }
  
  void ExperimentWithSomeEnum()
  {
    {
      printf("Attempting A\n");
      SomeEnum se(E_A);
      SomeEnumUsingFunction(se);
    }
    {
      printf("Attempting C\n");
      SomeEnum se(E_C);
      SomeEnumUsingFunction(se);
    }
    {
      printf("Attempting D\n");
      SomeEnum se(E_D);
      SomeEnumUsingFunction(se);
    }
    {
      printf("Attempting C | D\n");
  
  #ifdef SCENARIO_1
      // This next line below is simple, but gcc errors out with:
      //
      //   error: invalid conversion from ‘int’ to ‘SomeEnum’ [-fpermissive]
      //
      // GCC == c++ (GCC) 6.2.1 20160916 (Red Hat 6.2.1-3)
      SomeEnum se(E_A | E_D);
      SomeEnumUsingFunction(se);
  #endif
  
  #ifdef SCENARIO_2
      SomeEnum se(static_cast&lt;SomeEnum&gt;(E_A | E_D));
      SomeEnumUsingFunction(se);
  #endif
  
  #ifdef SCENARIO_3
      // This is a little better but still stinks as the caller _has_ to wrap the
      // value around "SomeEnum(...)" which is annoying.
      SomeEnum se(SomeEnum(E_A | E_D));
      SomeEnumUsingFunction(se);
  #endif
  
  #ifdef SCENARIO_4
      // OOOPS: Completely defeated!! Some lazy programmer can hack in "1 &lt;&lt; 8"
      // and change SomeEnumUsingFunction without having to change the header that
      // defines SomeEnum. I want to syntactically prevent them from being lazy
      // and hacking around the type system to avoid recompiling "the world" that
      // will necessarily occur when the header is changed.
      SomeEnum se(SomeEnum(E_A | E_D | (1 &lt;&lt; 8)));
      SomeEnumUsingFunction(se);
  #endif
  
    }
  }
  
  class SomeEnumImposter
  {
  public:
    SomeEnumImposter() : _additions(E_INVALID) {}
    // Using default copy constructor.
    // Using default operator=().
  
  #define define_getter_and_setter(X)             \
    SomeEnumImposter &amp; X()                        \
    {                                             \
      _additions = SomeEnum(_additions | E_##X);  \
      return *this;                               \
    }                                             \
    bool has##X()                                 \
    {                                             \
      return _additions &amp; E_##X;                  \
    }
  
    define_getter_and_setter(A);
    define_getter_and_setter(B);
    define_getter_and_setter(C);
    define_getter_and_setter(D);
  
  private:
    SomeEnum _additions;
  };
  
  void SomeEnumImposterUsingFunction(SomeEnumImposter se)
  {
    if ( se.hasC() || se.hasD() ) {
      printf("Has: C or D\n");
    }
  }
  
  void ExperimentWithSomeEnumImposter()
  {
    // Poor-mans assert():
    if ( ! (sizeof(SomeEnum) == sizeof(SomeEnumImposter)) ) {
      printf("%s:%d: ASSERTION FAILED: sizeof(SomeEnum) == sizeof(SomeEnumImposter)\n",__FILE__,__LINE__);
      return;
    }
  
    {
      printf("Attempting A\n");
      SomeEnumImposterUsingFunction(SomeEnumImposter().A());
    }
    {
      printf("Attempting C\n");
      SomeEnumImposterUsingFunction(SomeEnumImposter().C());
    }
    {
      printf("Attempting D\n");
      SomeEnumImposterUsingFunction(SomeEnumImposter().D());
    }
    {
      printf("Attempting C | D\n");
      SomeEnumImposterUsingFunction(SomeEnumImposter().C().D());
    }
  }
  
  struct Options {
    Options() : A(false), B(false), C(false), D(false) {}
    bool A;
    bool B;
    bool C;
    bool D;
  };
  
  void SomeOptionsUsingFunction(Options option_)
  {
    if ( option_.C || option_.D ) {
      printf("Has: C or D\n");
    }
  }
  
  void ExperimentWithClassWithBoolDatamembers()
  {
    {
      printf("Attempting A\n");
      Options options;
      options.A = true;
      SomeOptionsUsingFunction(options);
    }
    {
      printf("Attempting C\n");
      Options options;
      options.C = true;
      SomeOptionsUsingFunction(options);
    }
    {
      printf("Attempting D\n");
      Options options;
      options.D = true;
      SomeOptionsUsingFunction(options);
    }
    {
      printf("Attempting C | D\n");
      Options options;
      options.C = true;
      options.D = true;
      SomeOptionsUsingFunction(options);
    }
  }
  
  
  int main(int argc, char *argv[], char *const envp[])
  {
  #ifdef PLAIN_ENUM
    ExperimentWithSomeEnum();
  #endif
  #ifdef ENUM_IMPOSTER
    ExperimentWithSomeEnumImposter();
  #endif
  #ifdef CLASS_WITH_BOOL_DATAMEMBERS
    ExperimentWithClassWithBoolDatamembers();
  #endif
    return 0;
  }
  </code></pre>
  
  <p><code>compare.sh</code> contains:</p>
  
  <pre><code>#!/bin/bash
  
  compile_and_run () {
    local define_a_macro="$1"
    rm -f main.o
    /usr/bin/g++  -MD -DDEBUG -g $define_a_macro -ggdb -gstabs+ -O0  -fPIC  -Wall -Werror -Wsynth -Wno-comment -Wreturn-type   main.cpp -c -o main.o
    /usr/bin/g++  -MD -DDEBUG -g $define_a_macro -ggdb -gstabs+ -O0  -fPIC  -Wall -Werror -Wsynth -Wno-comment -Wreturn-type   main.o -L. -L/usr/lib64 -lstdc++  -o main.exe
    ./main.exe
  }
  
  echo
  /usr/bin/g++ --version
  
  set -e
  
  echo
  echo "PLAIN_ENUM:"
  (
    set -x -e
    compile_and_run -DPLAIN_ENUM
  )
  
  echo
  echo "ENUM_IMPOSTER:"
  (
    set -x -e
    compile_and_run -DENUM_IMPOSTER
  )
  
  echo
  echo "CLASS_WITH_BOOL_DATAMEMBERS:"
  (
    set -x -e
    compile_and_run -DCLASS_WITH_BOOL_DATAMEMBERS
  )
  </code></pre>
  
  <p>Running <code>./compare.sh</code> produces this output:</p>
  
  <pre><code>g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)
  Copyright (C) 2015 Free Software Foundation, Inc.
  This is free software; see the source for copying conditions.  There is NO
  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  
  
  PLAIN_ENUM:
  + compile_and_run -DPLAIN_ENUM
  + local define_a_macro=-DPLAIN_ENUM
  + rm -f main.o
  + /usr/bin/g++ -MD -DDEBUG -g -DPLAIN_ENUM -ggdb -gstabs+ -O0 -fPIC -Wall -Werror -Wsynth -Wno-comment -Wreturn-type main.cpp -c -o main.o
  + /usr/bin/g++ -MD -DDEBUG -g -DPLAIN_ENUM -ggdb -gstabs+ -O0 -fPIC -Wall -Werror -Wsynth -Wno-comment -Wreturn-type main.o -L. -L/usr/lib64 -lstdc++ -o main.exe
  + ./main.exe
  Attempting A
  Attempting C
  Has: C or D
  Attempting D
  Has: C or D
  Attempting C | D
  
  ENUM_IMPOSTER:
  + compile_and_run -DENUM_IMPOSTER
  + local define_a_macro=-DENUM_IMPOSTER
  + rm -f main.o
  + /usr/bin/g++ -MD -DDEBUG -g -DENUM_IMPOSTER -ggdb -gstabs+ -O0 -fPIC -Wall -Werror -Wsynth -Wno-comment -Wreturn-type main.cpp -c -o main.o
  + /usr/bin/g++ -MD -DDEBUG -g -DENUM_IMPOSTER -ggdb -gstabs+ -O0 -fPIC -Wall -Werror -Wsynth -Wno-comment -Wreturn-type main.o -L. -L/usr/lib64 -lstdc++ -o main.exe
  + ./main.exe
  Attempting A
  Attempting C
  Has: C or D
  Attempting D
  Has: C or D
  Attempting C | D
  Has: C or D
  
  CLASS_WITH_BOOL_DATAMEMBERS:
  + compile_and_run -DCLASS_WITH_BOOL_DATAMEMBERS
  + local define_a_macro=-DCLASS_WITH_BOOL_DATAMEMBERS
  + rm -f main.o
  + /usr/bin/g++ -MD -DDEBUG -g -DCLASS_WITH_BOOL_DATAMEMBERS -ggdb -gstabs+ -O0 -fPIC -Wall -Werror -Wsynth -Wno-comment -Wreturn-type main.cpp -c -o main.o
  + /usr/bin/g++ -MD -DDEBUG -g -DCLASS_WITH_BOOL_DATAMEMBERS -ggdb -gstabs+ -O0 -fPIC -Wall -Werror -Wsynth -Wno-comment -Wreturn-type main.o -L. -L/usr/lib64 -lstdc++ -o main.exe
  + ./main.exe
  Attempting A
  Attempting C
  Has: C or D
  Attempting D
  Has: C or D
  Attempting C | D
  Has: C or D
  </code></pre>