public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
* cast to an enum does not throw errors even for invalid values
@ 2007-05-25 11:26 Shriramana Sharma
  2007-05-25 14:38 ` Ian Lance Taylor
  2007-05-25 16:54 ` Young, Michael
  0 siblings, 2 replies; 9+ messages in thread
From: Shriramana Sharma @ 2007-05-25 11:26 UTC (permalink / raw)
  To: gcc-help

Using GCC 4.1.2.

(1)

Consider:

enum BODY { SUN, MOON, STAR } ;
enum PLANET { EARTH, VENUS, MARS, PLUTO } ;
int main ( void )
{
  BODY body ;
  // body = 1 ;                           // gives error. expected.
  // body = EARTH ;                       // gives error. expected.
  body = (BODY) 1 ;                       // no error. expected.
  body = (BODY) EARTH ;                   // no error. expected.
  body = static_cast < BODY > ( 1 ) ;     // no error. expected.
  body = static_cast < BODY > ( EARTH ) ; // no error. expected.
  body = (BODY) 3 ;                       // no error. unexpected.
  body = (BODY) PLUTO ;                   // no error. unexpected.
  body = static_cast < BODY > ( 3 ) ;     // no error. unexpected.
  body = static_cast < BODY > ( PLUTO ) ; // no error. unexpected.
}

I feel that the compiler should detect it when a value being casted to
an enum does not have an equivalent enum identifier. i.e. in the above
case, 3 and PLUTO (equivalent to 3 from the PLANET enum) do not have an
equivalent identifier in the BODY enum. But still the compiler does not
call an error. Even negative integers are "casted" to the target enum
without an error.

If an error is not called, I feel it defeats the very meaning of
casting, to convert an object of one type to an object of another type.
When there is no equivalent object of the target type, how can the
casting happen?

Even the behaviour of static_cast < unsigned int > ( -3 ) which gives
4294967293 is somehow understandable, since the internal binary
representation of signed int -3 and unsigned int 4294967293 is the same
(correct me if I'm wrong). So there can be said to be some kind of
"equivalence" which carries the casting to its goal. But how can (BODY)
3 or static_cast < BODY > ( 3 ) in the above example be carried out?

So it seems to me that there is a bug in G++.

(2)

Here's a test program for runtime case:

enum BODY { SUN, MOON, STAR } ;
enum PLANET { EARTH, VENUS, MARS, PLUTO } ;
void check ( PLANET planet )
{
  BODY body ;
  body = (BODY) planet ;
  body = static_cast < BODY > ( planet ) ;
}
int main ( void )
{
  check ( EARTH ) ;
  check ( PLUTO ) ;
}

The program executes without errors. It should throw a runtime error.

Both these are only in C++. In C, since enum-s are still fully
equivalent to int-s, the question of casting does not rise at all.

(3)

Incidentally, (unsigned int) (-3) in C (processed by GCC) gives me still
-3, and I don't know whether this is expected behaviour.

Thanks for your feedback. If it is judged by the community that any or
all of the behaviours I observe above is really a bug, I will report it.

Shriramana Sharma.



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: cast to an enum does not throw errors even for invalid values
  2007-05-25 11:26 cast to an enum does not throw errors even for invalid values Shriramana Sharma
@ 2007-05-25 14:38 ` Ian Lance Taylor
  2007-05-25 16:54 ` Young, Michael
  1 sibling, 0 replies; 9+ messages in thread
From: Ian Lance Taylor @ 2007-05-25 14:38 UTC (permalink / raw)
  To: Shriramana Sharma; +Cc: gcc-help

Shriramana Sharma <samjnaa@gmail.com> writes:

> Consider:
> 
> enum BODY { SUN, MOON, STAR } ;
> enum PLANET { EARTH, VENUS, MARS, PLUTO } ;
> int main ( void )
> {
>   BODY body ;
>   // body = 1 ;                           // gives error. expected.
>   // body = EARTH ;                       // gives error. expected.
>   body = (BODY) 1 ;                       // no error. expected.
>   body = (BODY) EARTH ;                   // no error. expected.
>   body = static_cast < BODY > ( 1 ) ;     // no error. expected.
>   body = static_cast < BODY > ( EARTH ) ; // no error. expected.
>   body = (BODY) 3 ;                       // no error. unexpected.
>   body = (BODY) PLUTO ;                   // no error. unexpected.
>   body = static_cast < BODY > ( 3 ) ;     // no error. unexpected.
>   body = static_cast < BODY > ( PLUTO ) ; // no error. unexpected.
> }
> 
> I feel that the compiler should detect it when a value being casted to
> an enum does not have an equivalent enum identifier. i.e. in the above
> case, 3 and PLUTO (equivalent to 3 from the PLANET enum) do not have an
> equivalent identifier in the BODY enum. But still the compiler does not
> call an error. Even negative integers are "casted" to the target enum
> without an error.

These conversions are permitted by the C++ language.  gcc could give a
warning, but not an error, and certainly not a runtime error.

Ian

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: cast to an enum does not throw errors even for invalid values
  2007-05-25 11:26 cast to an enum does not throw errors even for invalid values Shriramana Sharma
  2007-05-25 14:38 ` Ian Lance Taylor
@ 2007-05-25 16:54 ` Young, Michael
  1 sibling, 0 replies; 9+ messages in thread
From: Young, Michael @ 2007-05-25 16:54 UTC (permalink / raw)
  To: Shriramana Sharma, gcc-help

Shriramana Sharma wrote :
> If an error is not called, I feel it defeats the very meaning of
> casting, to convert an object of one type to an object of another type.
> When there is no equivalent object of the target type, how can the
> casting happen?

1) "the very meaning of casting" is not necessarily conversion, but coercion.
2) the underlying object type of an enum is an integral type - it's just restricted to a particular domain.  Casting from an integer to an integral type (even an enum) is allowable by C and C++ - you're not even going to get a run-time platform error/signal (typical for undefined behavior), as that would violate the behavior defined in the language standards.  There is an "equivalent object of the target type" here; just as you would expect to be able to cast (-3) to an unsigned short, you can cast it to an enum that doesn't have a defined domain value of -3.

Even if that was not the case (i.e., the target type is not compatible / equivalent), you wouldn't get an error from the compiler - and any run-time error would be caused because you subsequently invoke some functionality on the target object that is not available/compatible with the actual binary layout, not because of the coercion by itself.

When you cast (or static_cast or reinterpret_cast, but not dynamic_cast, in C++), you're saying, "hey, compiler, I'm the programmer and I know what I'm doing - allow me to break the type-safety rules here and treat this entity as if it were an X (even though in reality it may be a Y, where Y is compatible or incompatible).  Perhaps a warning for casting "out-of-domain-range" integer values to enum would be OK, but not an error - that affects too much existing code and violates standard definitions.  (I'm sure it's in the std, but I can't quote chapter and verse.)  But why even warn about "dangerous cast", when casting is inherently dangerous because you've explicitly "escaped" the type-safety system of the language?  The behavior you want is not even a candidate for an "extension", as the behavior is contrary to the existing language definition.

This is not a compiler bug - it is a behavior of C++ (and there's pros and cons of that behavior).  gcc/g++ strives to implement the language as defined in the standard - it's implementation is constrained by that definition.  Wishing, or even vigorously and sincerely insisting, otherwise doesn't change the facts.  (Yes, I've wished I could change certain things in C/C++, or the compiler, too.  Of course, the good news is that with gcc, you can, if you so choose!  If you actually change the behavior of the generated code, you are creating a non-std C++ variant, which I don't recommend - but that really is up to you!)

BTW - perhaps there's an easy workaround here if you do some OO analysis... I always really look at whether I need/want to use "enum", as I've discovered that, in my applications, they're really a C++ object "wanna-be".  If you define Planet as an object (implemented using an integer, perhaps), you can restrict the "domain" (name attribute) of that object, and you can control conversion via ctors and assignment operators.  Also, you can provide meaningful string output without a free (breaks encapsulation) decoder/conversion function that maps values to strings.  Consider also that there is no "inheritance" in enums - you can't define one enum as a superset of another without duplication / redefinition.  The only place I generally use enums anymore is for providing  readable parameters when invoking 3rd party APIs that use bit flag integer fields to specify behavior.  Finally, I've found that if you use macros, you can use an object type (class defintion) for development to provide debugging features and checks, but then switch to enums late in the development cycle for speed, just by changing the macro.  (Of course, the production version can't use any of the extended functionality provided by the class.)  However, I've found that I rarely change the macro definition, as my apps usually don't benefit from any speed gained by this change.  This technique may or may not be viable for you - it's probably less appealing if you are working with a large base of legacy code that would need to be refactored.

  - Michael

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: cast to an enum does not throw errors even for invalid values
  2007-05-29  0:23     ` John Love-Jensen
@ 2007-05-29 11:05       ` Gabriel Dos Reis
  0 siblings, 0 replies; 9+ messages in thread
From: Gabriel Dos Reis @ 2007-05-29 11:05 UTC (permalink / raw)
  To: John Love-Jensen; +Cc: Shriramana Sharma, MSX to GCC

John Love-Jensen <eljay@adobe.com> writes:

[...]

| > I feel GCC can implement the same for enum-s too.
| 
| GCC cannot implement the same for enum's too, because then the resulting
| language would be not-C++, which is not in the best interest of GNU and GCC.

[...]

| But if you want to pursue introducing a feature request to C++, please
| contact ISO/IEC JTC1/SC22/WG21 The C++ Standards Committee:
| 
| http://www.open-std.org/jtc1/sc22/wg21/

It's worth pointing out that enums will be "improved" in C++0x:

  http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2007/n2213.pdf

That is now part of the draft standard.  I believe Shriramana's issue
is addressed.  

-- Gaby

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: cast to an enum does not throw errors even for invalid values
  2007-05-28 14:17   ` Shriramana Sharma
  2007-05-28 17:33     ` Sergei Organov
@ 2007-05-29  0:23     ` John Love-Jensen
  2007-05-29 11:05       ` Gabriel Dos Reis
  1 sibling, 1 reply; 9+ messages in thread
From: John Love-Jensen @ 2007-05-29  0:23 UTC (permalink / raw)
  To: Shriramana Sharma; +Cc: MSX to GCC

Hi Shriramana,

> So the compiler itself checks whether it is possible to convert one
> struct value to another when a static cast is requested. Similarly for
> classes.

Then use classes and structs.

I usually wrap my enum's inside a class to perform the self-same kind of
validity checking that you are requesting.

> I feel GCC can implement the same for enum-s too.

GCC cannot implement the same for enum's too, because then the resulting
language would be not-C++, which is not in the best interest of GNU and GCC.

However, if these kinds of issues are important to you (and they seem to be;
and I have to admit that they are actually quite important to me too), I
strongly recommend taking a look at other languages that have far more
stringent type safety semantics such as Ada or D Programming Language.

Note: both Ada and D Programming Language are available in the GCC family.
Ada is part of the GCC in the gcc-ada-4.2.0.tar.bz2 front end, and D
Programming Language is available as a third party front end for GCC, via
SourceForge <http://dgcc.sourceforge.net/>.

Even Java J2SE 5.0 has impressively nice enum's.  But there's not a Java
J2SE 5.0 for the GCC family yet.  I may be mistaken. but I think there's
active, on-going work being done towards Java J2SE 5.0 support in the GCC
family.  Please bear in mind that Java support in GCC is tricky and in
arrears because Java is a moving target and GCC support of Java is often
after the "fact" (Sun's canonical Java release).

> If it is not part of the C++ definition, then please tell me where I can
> include a request for such a provision in the next C++ standard.

It's not part of the C++ definition.

I highly doubt it would even become part of the C++ definition, since it
would be a major breach of backwards compatibility.  Consider enum's used at
bitflags, which can be OR'd together.  Consider code that blithely
interchanges enum's with int's which otherwise is perfectly valid code.

But if you want to pursue introducing a feature request to C++, please
contact ISO/IEC JTC1/SC22/WG21 The C++ Standards Committee:

http://www.open-std.org/jtc1/sc22/wg21/

HTH,
--Eljay

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: cast to an enum does not throw errors even for invalid values
  2007-05-28 14:17   ` Shriramana Sharma
@ 2007-05-28 17:33     ` Sergei Organov
  2007-05-29  0:23     ` John Love-Jensen
  1 sibling, 0 replies; 9+ messages in thread
From: Sergei Organov @ 2007-05-28 17:33 UTC (permalink / raw)
  To: gcc-help

Shriramana Sharma <samjnaa@gmail.com> writes:
[...]
> If it is not part of the C++ definition, then please tell me where I
> can include a request for such a provision in the next C++ standard.

I'd start from learning the current standard. Most probably your
intention to request new feature to be added to C++ will vanish after
that automagically.

-- Sergei.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: cast to an enum does not throw errors even for invalid values
  2007-05-22 17:09 ` John Love-Jensen
@ 2007-05-28 14:17   ` Shriramana Sharma
  2007-05-28 17:33     ` Sergei Organov
  2007-05-29  0:23     ` John Love-Jensen
  0 siblings, 2 replies; 9+ messages in thread
From: Shriramana Sharma @ 2007-05-28 14:17 UTC (permalink / raw)
  To: John Love-Jensen; +Cc: MSX to GCC

John Love-Jensen wrote:

> That, obviously, is NOT what you desire.  You want some insurance that the
> right hand value is checked and verified to be cast to the left hand BODY
> value.
> It is, perhaps, unfortunate that the compiler does not synthesize this for
> you.  But that's just not C++.

See the following code:

struct Struct1
{
     int a, b ;
} ;
struct Struct2
{
     int a, b ;
} ;
int main ( void )
{
     Struct1 s1 ; Struct2 s2 ;
     s1 . a = 2 ; s1 . b = 3 ;
     // s2 = static_cast < Struct2 > ( s1 ) ; // gives error: no 
matching function for call to ‘Struct2::Struct2(Struct1&)’
}

So the compiler itself checks whether it is possible to convert one 
struct value to another when a static cast is requested. Similarly for 
classes. I feel GCC can implement the same for enum-s too.

If it is not part of the C++ definition, then please tell me where I can 
include a request for such a provision in the next C++ standard.

Shriramana Sharma.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: cast to an enum does not throw errors even for invalid values
  2007-05-22 13:00 Shriramana Sharma
@ 2007-05-22 17:09 ` John Love-Jensen
  2007-05-28 14:17   ` Shriramana Sharma
  0 siblings, 1 reply; 9+ messages in thread
From: John Love-Jensen @ 2007-05-22 17:09 UTC (permalink / raw)
  To: Shriramana Sharma, MSX to GCC

Hi Shriramana,

> If an error is not called, I feel it defeats the very meaning of
> casting, to convert an object of one type to an object of another type.
> When there is no equivalent object of the target type, how can the
> casting happen?

It appears you have misunderstood what a cast operation means in this
situation.  Both the C-style cast, and the static_cast.

It means that you, the programmer, have vouched that the value being cast is
appropriate for what is being cast to.

That, obviously, is NOT what you desire.  You want some insurance that the
right hand value is checked and verified to be cast to the left hand BODY
value.

You should write and use this routine:

BODY ConvertToBODY(int i)
{
  BODY result = SUN;

  switch(i)
  {
    case SUN: result = SUN; break;
    case MOON: result = MOON; break;
    case STAR: result = STAR; break;
    default: throw "out of BODY range error";
  }

  return result;
}

It is, perhaps, unfortunate that the compiler does not synthesize this for
you.  But that's just not C++.

HTH,
--Eljay

^ permalink raw reply	[flat|nested] 9+ messages in thread

* cast to an enum does not throw errors even for invalid values
@ 2007-05-22 13:00 Shriramana Sharma
  2007-05-22 17:09 ` John Love-Jensen
  0 siblings, 1 reply; 9+ messages in thread
From: Shriramana Sharma @ 2007-05-22 13:00 UTC (permalink / raw)
  To: gcc-help

Using GCC 4.1.2.

(1)

Consider:

enum BODY { SUN, MOON, STAR } ;
enum PLANET { EARTH, VENUS, MARS, PLUTO } ;
int main ( void )
{
  BODY body ;
  // body = 1 ;                           // gives error. expected.
  // body = EARTH ;                       // gives error. expected.
  body = (BODY) 1 ;                       // no error. expected.
  body = (BODY) EARTH ;                   // no error. expected.
  body = static_cast < BODY > ( 1 ) ;     // no error. expected.
  body = static_cast < BODY > ( EARTH ) ; // no error. expected.
  body = (BODY) 3 ;                       // no error. unexpected.
  body = (BODY) PLUTO ;                   // no error. unexpected.
  body = static_cast < BODY > ( 3 ) ;     // no error. unexpected.
  body = static_cast < BODY > ( PLUTO ) ; // no error. unexpected.
}

I feel that the compiler should detect it when a value being casted to 
an enum does not have an equivalent enum identifier. i.e. in the above 
case, 3 and PLUTO (equivalent to 3 from the PLANET enum) do not have an 
equivalent identifier in the BODY enum. But still the compiler does not 
call an error. Even negative integers are "casted" to the target enum 
without an error.

If an error is not called, I feel it defeats the very meaning of 
casting, to convert an object of one type to an object of another type. 
When there is no equivalent object of the target type, how can the 
casting happen?

Even the behaviour of static_cast < unsigned int > ( -3 ) which gives 
4294967293 is somehow understandable, since the internal binary 
representation of signed int -3 and unsigned int 4294967293 is the same 
(correct me if I'm wrong). So there can be said to be some kind of 
"equivalence" which carries the casting to its goal. But how can (BODY) 
3 or static_cast < BODY > ( 3 ) in the above example be carried out?

So it seems to me that there is a bug in G++.

(2)

Here's a test program for runtime case:

enum BODY { SUN, MOON, STAR } ;
enum PLANET { EARTH, VENUS, MARS, PLUTO } ;
void check ( PLANET planet )
{
  BODY body ;
  body = (BODY) planet ;
  body = static_cast < BODY > ( planet ) ;
}
int main ( void )
{
  check ( EARTH ) ;
  check ( PLUTO ) ;
}

The program executes without errors. It should throw a runtime error.

Both these are only in C++. In C, since enum-s are still fully 
equivalent to int-s, the question of casting does not rise at all.

(3)

Incidentally, (unsigned int) (-3) in C (processed by GCC) gives me still 
-3, and I don't know whether this is expected behaviour.

Thanks for your feedback. If it is judged by the community that any or 
all of the behaviours I observe above is really a bug, I will report it.

Shriramana Sharma.

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2007-05-29  0:23 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-05-25 11:26 cast to an enum does not throw errors even for invalid values Shriramana Sharma
2007-05-25 14:38 ` Ian Lance Taylor
2007-05-25 16:54 ` Young, Michael
  -- strict thread matches above, loose matches on Subject: below --
2007-05-22 13:00 Shriramana Sharma
2007-05-22 17:09 ` John Love-Jensen
2007-05-28 14:17   ` Shriramana Sharma
2007-05-28 17:33     ` Sergei Organov
2007-05-29  0:23     ` John Love-Jensen
2007-05-29 11:05       ` Gabriel Dos Reis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).