Query regarding enums in switch/case statements.

K

Kurt

Hi. I have a c++ project that is basically a set of implementation
hiding classes, so that we can convert a static lib with a lot of
dependancies into a dynamic lib with less dependancies, so that users
of the lib dont require all the development tools we used to develop
the lib.

I have noticed something strange, and was wondering if its a C++
feature, or a implementation specific bug.

I have a function like this:

CheckerErrorId CCheckerError::GetErrorId()
{
ECheckerError ce = pimpl->getC_ErrorId();
switch (ce)
{
case CINFO:
return CINFO;
case CHWARNING_bus_blocked_yet:
return CHWARNING_bus_blocked_yet;
case CHWARNING_no_output_message_defined:
return CHWARNING_no_output_message_defined;
// Rest of list omitted.
default:
return CHERROR_unknown;
}
}

This function converts the internally used ECheckerError enum (as
defined in the original static libs header files) into the equivalent
CheckerErrorId as defined in my dynamic lib.

However the compiler seems to treat ce as if it were a CheckerErrorId
rather than a ECheckerError. pimpl->getC_ErrorId() could return CINFO,
but the switch statement seems to skip to the case that CINFO matches
in the list of Checker.

Internal enum looks something like this (most of it removed):

enum ECheckerError {
CHERROR_unknown,
CHWARNING_bus_blocked_yet,
CHWARNING_no_output_message_defined,
CINFO
};

CheckerErrorId looks like :
enum CheckerErrorId {
CHERROR_unknown,
CHWARNING_bus_blocked_yet,
CHWARNING_bus_undefined,
CHWARNING_no_output_message_defined,
TPCHWARNING_wrong_generalTimeout_time,
CINFO
};

So if pimpl->getC_ErrorId() returns CINFO, then it jumps down to the
case for CHWARNING_no_output_message_defined.

If we use if and == then the problem remains. The compiler seems to
ignore the fact that ce is a ECheckerError, and treats it like it were
a CheckerErrorId, or perhaps as if they were merely #defines or
something, simply replacing them by their integer representations!

My question is, is this standard c++ behavior or a possible compiler
bug?

Thanks a lot gurus.

Kurt
 
M

mlimber

Hi. I have a c++ project that is basically a set of implementation
hiding classes, so that we can convert a static lib with a lot of
dependancies into a dynamic lib with less dependancies, so that users
of the lib dont require all the development tools we used to develop
the lib.

I have noticed something strange, and was wondering if its a C++
feature, or a implementation specific bug.

I have a function like this:

CheckerErrorId CCheckerError::GetErrorId()
{
ECheckerError ce = pimpl->getC_ErrorId();
switch (ce)
{
case CINFO:
return CINFO;
case CHWARNING_bus_blocked_yet:
return CHWARNING_bus_blocked_yet;
case CHWARNING_no_output_message_defined:
return CHWARNING_no_output_message_defined;
// Rest of list omitted.
default:
return CHERROR_unknown;
}

}

This function converts the internally used ECheckerError enum (as
defined in the original static libs header files) into the equivalent
CheckerErrorId as defined in my dynamic lib.

However the compiler seems to treat ce as if it were a CheckerErrorId
rather than a ECheckerError. pimpl->getC_ErrorId() could return CINFO,
but the switch statement seems to skip to the case that CINFO matches
in the list of Checker.

Internal enum looks something like this (most of it removed):

enum ECheckerError {
CHERROR_unknown,
CHWARNING_bus_blocked_yet,
CHWARNING_no_output_message_defined,
CINFO

};

CheckerErrorId looks like :
enum CheckerErrorId {
CHERROR_unknown,
CHWARNING_bus_blocked_yet,
CHWARNING_bus_undefined,
CHWARNING_no_output_message_defined,
TPCHWARNING_wrong_generalTimeout_time,
CINFO

};

So if pimpl->getC_ErrorId() returns CINFO, then it jumps down to the
case for CHWARNING_no_output_message_defined.

If we use if and == then the problem remains. The compiler seems to
ignore the fact that ce is a ECheckerError, and treats it like it were
a CheckerErrorId, or perhaps as if they were merely #defines or
something, simply replacing them by their integer representations!

My question is, is this standard c++ behavior or a possible compiler
bug?

Obviously you can't have two enum constants with the same name in the
same scope. So, for instance:

enum E1 { A };
struct S
{
enum E2 { A }; // Ok: different scope
};
enum E3 { A }; // Error: A already exists in this scope

In your function since the compiler would complain if your return
value type wasn't convertible to a CheckerErrorId, apparently the enum
constants for CheckerErrorId are in scope but those for ECheckerError
are not (but then how is the enum type ECheckerError unqualified? Is
that your real code or did you [incorrectly] modify it for this
post?).

Please clarify, and check out this FAQ on posting code that doesn't
work properly:

http://www.parashift.com/c++-faq-lite/how-to-post.html#faq-5.8

Cheers! --M
 
K

Kurt

Obviously you can't have two enum constants with the same name in the
same scope. So, for instance:

enum E1 { A };
struct S
{
enum E2 { A }; // Ok: different scope
};
enum E3 { A }; // Error: A already exists in this scope

In your function since the compiler would complain if your return
value type wasn't convertible to a CheckerErrorId, apparently the enum
constants for CheckerErrorId are in scope but those for ECheckerError
are not (but then how is the enum type ECheckerError unqualified? Is
that your real code or did you [incorrectly] modify it for this
post?).

Please clarify, and check out this FAQ on posting code that doesn't
work properly:

Thanks a lot for the fast response. Thats the real code, except I have
taken out most of the enum entries. As far as scope goes,
CheckerErrorId is defined in a namespace, ECheckerError not, but I
think they are both in scope. Seems like at compile time theres no
issue, why should the compiler expect ECheckerError to be convertible
to a CheckerErrorId? Theres no explicit assignment or comparison
between the two.

Surely the type safety mechanism makes it clear that the only thing
following the case can be a ECheckerError, and the only things that
can be returned are CheckerErrorIds?

The issue only really appears at run time, when the type of ce seems
to be ignored.

Or perhaps the error is that my compiler neglects to detect any scope
collision.

Ahh it is the namespace thing isnt it. If I am "using" the namespace,
then the ECheckerError enums should be preceeded by a :: right? Heh
amazing how typing out a newsgroup article helps one think things
over.

But still... Typesafety should have provided the compiler and/or
runtime system with sufficient information to distinguish which is
which.. ce is declared as a ECheckerError, the compiler shouldnt even
consider that anything following a case statement is anything else.
 
M

mlimber

Obviously you can't have two enum constants with the same name in the
same scope. So, for instance:
enum E1 { A };
struct S
{
enum E2 { A }; // Ok: different scope
};
enum E3 { A }; // Error: A already exists in this scope
In your function since the compiler would complain if your return
value type wasn't convertible to a CheckerErrorId, apparently the enum
constants for CheckerErrorId are in scope but those for ECheckerError
are not (but then how is the enum type ECheckerError unqualified? Is
that your real code or did you [incorrectly] modify it for this
post?).
Please clarify, and check out this FAQ on posting code that doesn't
work properly:

Thanks a lot for the fast response. Thats the real code, except I have
taken out most of the enum entries. As far as scope goes,
CheckerErrorId is defined in a namespace, ECheckerError not, but I
think they are both in scope. Seems like at compile time theres no
issue, why should the compiler expect ECheckerError to be convertible
to a CheckerErrorId? Theres no explicit assignment or comparison
between the two.

Surely the type safety mechanism makes it clear that the only thing
following the case can be a ECheckerError, and the only things that
can be returned are CheckerErrorIds?

The issue only really appears at run time, when the type of ce seems
to be ignored.

Or perhaps the error is that my compiler neglects to detect any scope
collision.

Ahh it is the namespace thing isnt it. If I am "using" the namespace,
then the ECheckerError enums should be preceeded by a :: right? Heh
amazing how typing out a newsgroup article helps one think things
over.

But still... Typesafety should have provided the compiler and/or
runtime system with sufficient information to distinguish which is
which.. ce is declared as a ECheckerError, the compiler shouldnt even
consider that anything following a case statement is anything else.

Does your compiler allow this:

enum E1 { A };
namespace NS { enum E2 { A }; }
using namespace NS;

It shouldn't because A is ambiguous. Hence, I don't think you've
assessed your scope problem correctly. Please post a *complete* but
*minimal* sample (i.e., one that we can cut and paste into our editors
unchanged) that demonstrates your problem.

In any case, this should work fine:

enum E1 { A, B };
namespace NS { enum E2 { A, B }; }

NS::E2 Foo( E1 e1 )
{
switch( e1 )
{
case NS::A: // Not ::A
return NS::A;
default:
return NS::B;
}
}

The reason is that enums are convertible to and therefore comparable
as ints, so the compiler won't complain about the type in such a
switch statement. See also:

http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.19
http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.20

Cheers! --M
 
K

Kurt

Does your compiler allow this:

enum E1 { A };
namespace NS { enum E2 { A }; }
using namespace NS;

It shouldn't because A is ambiguous. Hence, I don't think you've
assessed your scope problem correctly. Please post a *complete* but
*minimal* sample (i.e., one that we can cut and paste into our editors
unchanged) that demonstrates your problem.

Heh my compiler has no problem at all with it. (MS VC++ 2005 without
the buggy SP1)

However when I do something like:

enum E1 { C, A, B };
namespace NS { enum E2 { A, B, C, D, E }; }
using namespace NS;

E2 Foo( E1 e1 )
{
switch( e1 )
{
case A:
return A;
default:
return B;
}
}

Which is almost analogous to my situation, then I get ambiguous symbol
errors. Still trying to work out exactly why my real code doesnt
generate this error...

Anyway, it was no real problem, I just renamed all the entries in my
CheckerErrorId by prefixing them with something so they dont clash
with the ECheckerError.

Thanks a lot for your comments.

OK! Now I have the analagous example: The difference was that Foo is a
member of a class.

enum E1 { C, A, B };
namespace NS { enum E2 { A, B, C, D, E };
class Bar
{
virtual E2 Foo( E1 e1);
};
}
using namespace NS;

E2 Bar::Foo( E1 e1 )
{
switch( e1 )
{
case A:
return A;
default:
return B;
}
}

This compiles fine for me, with no ambiguous symbol errors. Compiler
bug?
 
K

Kurt

enum E1 { C, A, B };
namespace NS { enum E2 { A, B, C, D, E };
class Bar
{
virtual E2 Foo( E1 e1);
};}

using namespace NS;

E2 Bar::Foo( E1 e1 )
{
switch( e1 )
{
case A:
return A;
default:
return B;
}
}

This compiles fine for me, with no ambiguous symbol errors. Compiler
bug?

Hmm GCC on linux has no problem with it either, what difference here
does making Foo a class member make?

Not really trying to solve any problems here as much as just improve
my understanding of c++.
 
M

Michael Mehlich

Kurt said:
Hmm GCC on linux has no problem with it either, what difference here
does making Foo a class member make?

Not really trying to solve any problems here as much as just improve
my understanding of c++.

Actually, the difference is that Foo is (indirectly) enclosed by
the namespace NS, causing a lookup for the enumerator constants A
and B from *within* the namespace (more precisely, from a subscope
within that namespace).

As E2 is defined within the namespace its enumerators shadow
identically named enumerators in namespaces (including the global
namespace) enclosing NS.
 
M

mlimber

Heh my compiler has no problem at all with it. (MS VC++ 2005 without
the buggy SP1)

Mea culpa. I should have added a use of A after it, like:

int main()
{
return A;
}

Now Comeau, VC++ '05, EDG, and gcc give an error.
OK! Now I have the analagous example: The difference was that Foo is a
member of a class.

enum E1 { C, A, B };
namespace NS { enum E2 { A, B, C, D, E };
class Bar
{
virtual E2 Foo( E1 e1);
};}

using namespace NS;

E2 Bar::Foo( E1 e1 )
{
switch( e1 )
{
case A:
return A;
default:
return B;
}
}

This compiles fine for me, with no ambiguous symbol errors. Compiler
bug?

Ok, now we're down to the real problem. As Michael Mehlich said, the
issue is not that Foo is a member function but that Foo is in the same
namespace with E2, which silently hides E1's constants since their
outside the namespace. (PC-Lint by Gimpel catches things like this,
BTW. You might want to check it out.) And like I said previously,
there's no type error here because e1 is compared *as an int* against
the *integer values* of E2's A and B but the proper enum type is still
returned.

Cheers! --M
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,816
Latest member
SapanaCarpetStudio

Latest Threads

Top