3

C++ is mostly a superset of C, but not always. In particular, while enumeration values in both C and C++ implicitly convert into int, the reverse isn't true: only in C do ints convert back into enumeration values. Thus, bitflags defined via enumeration declarations don't work correctly. Hence, this is OK in C, but not in C++:

typedef enum Foo
{
    Foo_First = 1<<0,
    Foo_Second = 1<<1,
} Foo;

int main(void)
{
    Foo x = Foo_First | Foo_Second; // error in C++
    return 0;
}

How should this problem be handled efficiently and correctly, ideally without harming the debugger-friendly nature of using Foo as the variable type (it decomposes into the component bitflags in watches etc.)?

Consider also that there may be hundreds of such flag enumerations, and many thousands of use-points. Ideally some kind of efficient operator overloading would do the trick, but it really ought to be efficient; the application I have in mind is compute-bound and has a reputation of being fast.

Clarification: I'm translating a large (>300K) C program into C++, so I'm looking for an efficient translation in both run-time and developer-time. Simply inserting casts in all the appropriate locations could take weeks.

Barry Kelly
  • 41,404
  • 5
  • 117
  • 189

3 Answers3

9

Why not just cast the result back to a Foo?

Foo x = Foo(Foo_First | Foo_Second);

EDIT: I didn't understand the scope of your problem when I first answered this question. The above will work for doing a few spot fixes. For what you want to do, you will need to define a | operator that takes 2 Foo arguments and returns a Foo:

Foo operator|(Foo a, Foo b)
{
    return Foo(int(a) | int(b));
}

The int casts are there to prevent undesired recursion.

Ferruccio
  • 98,941
  • 38
  • 226
  • 299
  • Going through hundreds of thousands of lines of source code inserting casts is not my idea of an efficient translation. – Barry Kelly Oct 14 '08 at 00:53
  • @Ferruccio, wouldn't the syntax be "Foo x = (Foo)(Foo_First | Foo_Second);" – Onorio Catenacci Oct 14 '08 at 00:59
  • You can do that too. I used a C++ style cast. C style casts still work. – Ferruccio Oct 14 '08 at 01:09
  • If you have a Foo operator|(Foo, Foo), why would you need to cast (Foo_First | Foo_Second) ? – aib Oct 14 '08 at 01:12
  • You don't. I was describing two different solutions. You either cast each instance to a Foo or you implement operator| for Foos. You don't need to do both. – Ferruccio Oct 14 '08 at 01:15
  • The operator| is the perfect solution here. You are explicitly saying that it is OK to bitwise-or the members of this particular enum. The runtime behavior ends up the sames as it would in C, including compile-time constant folding, as long as the operator definition is in scope. – Frank Szczerba Oct 14 '08 at 19:16
2

It sounds like an ideal application for a cast - it's up to you to tell the compiler that yes, you DO mean to instantiate a Foo with a random integer.

Of course, technically speaking, Foo_First | Foo_Second isn't a valid value for a Foo.

Mike G.
  • 1,670
  • 11
  • 17
0

Either leave the result as an int or static_cast:

Foo x = static_cast<Foo>(Foo_First | Foo_Second); // not an error in C++
ejgottl
  • 2,809
  • 19
  • 18