How should C bitflag enumerations be translated into C++?
Asked Answered
T

3

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.

Tupi answered 14/10, 2008 at 0:45 Comment(0)
B
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.

Bespread answered 14/10, 2008 at 0:49 Comment(6)
Going through hundreds of thousands of lines of source code inserting casts is not my idea of an efficient translation.Tupi
@Ferruccio, wouldn't the syntax be "Foo x = (Foo)(Foo_First | Foo_Second);"Insupportable
You can do that too. I used a C++ style cast. C style casts still work.Bespread
If you have a Foo operator|(Foo, Foo), why would you need to cast (Foo_First | Foo_Second) ?Eternize
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.Bespread
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.Poverty
H
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.

Halfslip answered 14/10, 2008 at 0:50 Comment(3)
in this case, it is - 1|0 is still 1Cavatina
The idiom is valid in C, and I am trying to translate a large (>300K) C program into C++, so I'm looking for an efficient translation in both run-time and developer-time.Tupi
@Cavatina - 1<<0 == 1, 1<<1 == 2; it is not 1|0, but 1|2 == 3.Tupi
N
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++
Nanice answered 14/10, 2008 at 0:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.