Topic: tricky plain and signed integer questions


Author: "Me" <anti_spam_email2003@yahoo.com>
Date: 2 Jun 2005 21:40:18 GMT
Raw View
(I moved this question over to comp.std.c++ from
comp.lang.c++.moderated)

template<class T>
struct si {
        static T m;

};

&si<int>::m == &si<signed int>::m

// Is that expression always true or never true (or something else)?

template<class T>
struct bi {
        T foo : 4;

};

bi<int>.foo;

// can bi<int>::foo be unsigned on compilers that treat plain int
// bitfields as unsigned?

template<class Ty>
struct ty1 {
        typedef Ty T;

};

template<class Ty>
struct ty2 {
        typedef Ty T;

};

struct foo {
        ty1<int>::T a : 4;
        ty1<signed int>::T b : 4;
        ty2<signed int>::T c : 4;
        ty2<int>::T d : 4;

};

// Does order of usage matter? What are the possibilities for struct
// foo?

If the answer to all of these questions is yes, you have an ODR
violation if ty1<signed int> was seen before foo's definition in a
translation unit. This case doesn't seem to be covered by the standard.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: v.Abazarov@comAcast.net ("Victor Bazarov")
Date: Fri, 3 Jun 2005 14:46:30 GMT
Raw View
Me wrote:
> (I moved this question over to comp.std.c++ from
> comp.lang.c++.moderated)
>
> template<class T>
> struct si {
>        static T m;
>
> };
>
> &si<int>::m == &si<signed int>::m
>
> // Is that expression always true or never true (or something else)?

'int' and 'signed int' are the same type.  My guess would be that the
expression evaluates to 'true' in compile-time.  See 7.1.5.2.

> template<class T>
> struct bi {
>        T foo : 4;
>
> };
>
> bi<int>.foo;

Outside of any function you're only allowed to have _declarations_.
The statement above is not a declaration.

> // can bi<int>::foo be unsigned on compilers that treat plain int
> // bitfields as unsigned?

If there is such compiler, it's broken.  'int' and 'unsigned int' are
different types, including when used to define bit fields.  7.1.5.2.

> template<class Ty>
> struct ty1 {
>        typedef Ty T;
>
> };
>
> template<class Ty>
> struct ty2 {
>        typedef Ty T;
>
> };
>
> struct foo {
>        ty1<int>::T a : 4;
>        ty1<signed int>::T b : 4;
>        ty2<signed int>::T c : 4;
>        ty2<int>::T d : 4;
>
> };
>
> // Does order of usage matter?

The order of usage of what?  'ty1<int>::T' and 'ty1<signed int>::T'
are the same type, 'int'.  Same for 'ty2'.

> What are the possibilities for struct
> // foo?

Possibilities of what?

> If the answer to all of these questions is yes,

You have multiple choice questions which cannot be answered 'yes' or
'no'.  Or did you mean, "if the answer to _both_ quesitons is yes"?

> you have an ODR
> violation if ty1<signed int> was seen before foo's definition in a
> translation unit. This case doesn't seem to be covered by the
> standard.

I guess I either don't know enough C++ or you weren't so unclear, that
all I have to say here is "Huh?"

V


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Clark S. Cox III" <clarkcox3@gmail.com>
Date: Fri, 3 Jun 2005 11:16:28 CST
Raw View
On 2005-06-02 17:40:18 -0400, "Me" <anti_spam_email2003@yahoo.com> said:

> (I moved this question over to comp.std.c++ from
> comp.lang.c++.moderated)
>
> template<class T>
> struct si {
>         static T m;
>
> };
>
> &si<int>::m == &si<signed int>::m
>
> // Is that expression always true or never true (or something else)?

It's always true. 'int' and 'signed int' are the exact same type;
therefore si<int> and si<signed int> are the exact same type. I believe
that should answer all of your questions that follow.

To sum up:
 'int' and 'signed int' are the exact same type.
 'unsigned' and 'unsigned int' are the exact same type.
 'short', 'signed short', 'short int' and 'signed short int' are the same type.
 'unsigned short' and 'unsigned short int' are the exact same type.
 'long', 'signed long', 'long int' and 'signed long int' are the same type.
 'unsigned long' and 'unsigned long int' are the exact same type.
 But remember that 'signed char', 'unsigned char' and 'char' are three
*different* types.


--
Clark S. Cox, III
clarkcox3@gmail.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Alberto Barbati <AlbertoBarbati@libero.it>
Date: 3 Jun 2005 22:40:02 GMT
Raw View
Victor Bazarov wrote:
> Me wrote:
>
>
>>// can bi<int>::foo be unsigned on compilers that treat plain int
>>// bitfields as unsigned?
>
> If there is such compiler, it's broken.  'int' and 'unsigned int' are
> different types, including when used to define bit fields.  7.1.5.2.
>

Not exactly and that's precisely the source of all the OP's trouble.
   9.6/3 says "It is implementation-defined whether a plain (neither
explicitly signed nor unsigned) char, short, int or long bit-field is
signed or unsigned."

In practice:

struct Bitfields
{
  unsigned int f1 : 4; // guaranteed to be unsigned
  signed int   f2 : 4; // guaranteed to be signed
  int          f3 : 4; // unspecified: could be signed or unsigned
};

This strange exception contradicts the complete equivalence between
signed int and int described in 7.1.5.2.

The rest of the OP's questions all boils down to this: in the following
code:

  template<class Ty> struct A { typedef Ty T; };
  struct B { A<signed int>::T f : 4; };

is f guaranteed to be signed or not? If it is, can this code produce a
different result:

  template<class Ty> struct A { typedef Ty T; };
  struct B { A<int>::T f : 4; };

though A<int> and A<signed int> are de facto the same type?

The particular compiler the OP is using seems to "remember" the
signed-ness of the type first used for instantiation and this just
confuses things even more. In particular, with that compiler you have
(if I've understood it correctly):

  template<class Ty> struct A { typedef Ty T; };
  struct B {
    A<int>::T        f1 : 4;  // f1 is unsigned
    A<signed int>::T f2 : 4;  // refers to A<int>, f2 also unsigned
  };

and

  template<class Ty> struct A { typedef Ty T; };
  struct B {
    A<signed int>::T f1 : 4;  // f1 is signed
    A<int>::T        f2 : 4;  // refers to A<signed int>, f2 also signed
  };

and this hurts (not to mention potential ODR issues).

I guess all doubts would be lifted if the C++ standard adopted a more
explicit wording similar to one used in the C99 standard (   6.7.2/8): "A
bit-field shall have a type that is a qualified or unqualified version
of _Bool, signed int, or unsigned int. [Note: if the actual type
specifier used is int or a typedef-name defined as int, then it is
implementation-defined whether the bit-field is signed or unsigned.]"

With this wording it's unambiguously stated that the signed "quality" is
somehow "lost" when making typedefs, so that

  struct B { A<signed int>::T f : 4; };

shall be in all respects equivalent to

  struct B { int f : 4; };

It's still unspecified whether f is signed or unsigned but at least the
code will not silently change behaviour if someone inserts some use of
A<int> before the first use of A<signed int>.

BTW, with this clarification, the OP's compiler would thus be
non-conformant.

Alberto

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Me" <anti_spam_email2003@yahoo.com>
Date: 4 Jun 2005 03:40:05 GMT
Raw View
> It's always true. 'int' and 'signed int' are the exact same type;
> therefore si<int> and si<signed int> are the exact same type. I believe
> that should answer all of your questions that follow.

Except in bit-fields 9.6/3 - "It is implementation-defined whether a
plain (neither explicitly signed nor unsigned) char, short, int or long
bit-field is signed or unsigned.". The C99 standard explicitly says
that this is still true when typedefs to a plain integer are involved
(6.7.7/6). I can't find anything in the C++ standard that either
explicitly agrees or disagrees but I'm going to go with what the C99
standard says here. To see why this is a problem, take this code for
instance:

/// ty.h

struct plain {
 int a : 4;
};

template<class T>
struct ty1 {
 typedef T ty;
 T a : 4;
};

/// foo.h

#include <iostream>

struct foo {
 ty1<int>::ty a : 4;
};

inline void foo_fn()
{
 foo f;
 f.a = -1;
// implementation defined if it prints -1 or 15
 std::cout << f.a;
}

See the problem?

The standard says it is implementation defined whether plain::a is
signed or unsigned (9.6/3), i.e. its range is either [-7,7] or [-8,7]
(depending representation/padding/traps) or [0, 15]. For the sake of
discussion, lets say our compiler makes plain bit-fields unsigned and
that it uses 2s complement with no padding/traps for signed integers.

ty1<int> is the same type as ty1<signed int> because signed int is the
same as int, except in bit-fields, but uh-oh ty1 contains a bit-field.
Taking what the C standard says (6.7.7/6) about plain typedefs (and
naturally extending this to template parameters) into account, we see:

plain::a's range is [0,15]
ty1<int>::a's range is [0,15]
ty1<signed int>::a's range is [-8,7]

In order for ty1<int> to be ty1<signed int>, a's range has to be
identical. Something must give, either typedefs/template parameters
become signed, order of usage matters (my compiler picks this but then
this means ty1<signed int>::a could have the range [0,15]), or
something else. If order of usage matters:

/// a.cpp

#include "ty.h"
#include "foo.h"

void b()
{
 foo_fn();
}

/// b.cpp

#include "ty.h"
ty1<signed int> global_ty;
#include "foo.h"

void b()
{
 foo_fn();
}

In this b.cpp's translation unit, ty1<int> gets converted to ty1<signed
int>, which means not only is there an ODR violation on foo but there
is one on the foo_fn() function as well. This case doesn't seem to be
covered by any of the ODR violation rules in the standard.

I think the standard is ambiguous about all of this unless anyone can
quote specific paragraphs that are relevant.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Victor Bazarov" <v.Abazarov@comAcast.net>
Date: Sat, 4 Jun 2005 09:56:59 CST
Raw View
Alberto Barbati wrote:
> Victor Bazarov wrote:
>> Me wrote:
>>
>>
>>> // can bi<int>::foo be unsigned on compilers that treat plain int
>>> // bitfields as unsigned?
>>
>> If there is such compiler, it's broken.  'int' and 'unsigned int' are
>> different types, including when used to define bit fields.  7.1.5.2.
>>
>
> Not exactly and that's precisely the source of all the OP's trouble.
>    9.6/3 says "It is implementation-defined whether a plain (neither
> explicitly signed nor unsigned) char, short, int or long bit-field is
> signed or unsigned."
>
> In practice:
>
> struct Bitfields
> {
>  unsigned int f1 : 4; // guaranteed to be unsigned
>  signed int   f2 : 4; // guaranteed to be signed
>  int          f3 : 4; // unspecified: could be signed or unsigned
> };
>
> This strange exception contradicts the complete equivalence between
> signed int and int described in 7.1.5.2.
>
> [...]

My guess is the OP found a contradition in the Standard that needs to be
resolved.  I vote to leave the signedness of only 'char' to the
implementation, and all other integral types' signedness should be what
they already are, IOW, leave 7.1.5.2 as is and fix 9.6/3.  There is one
special case, of course.  If you have a bit field with width of 1 and
declared 'int' ('signed int'), then what does the single bit mean?  If
it's 0, it's 0, there is no ambiguity.  But if it's 1, is that the sign
bit or is that the magnitude?  Is that the "all bits set", which gets
interpreted differently on different architectures?  However, if those
questions have already been answered for explicitly signed types, there
should be no problem with types that are implicitly signed, right?

V


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: kanze@gabi-soft.fr
Date: Mon, 6 Jun 2005 10:13:56 CST
Raw View
Me wrote:
> > It's always true. 'int' and 'signed int' are the exact same
> > type; therefore si<int> and si<signed int> are the exact
> > same type. I believe that should answer all of your
> > questions that follow.

> Except in bit-fields 9.6/3 - "It is implementation-defined
> whether a plain (neither explicitly signed nor unsigned) char,
> short, int or long bit-field is signed or unsigned.". The C99
> standard explicitly says that this is still true when typedefs
> to a plain integer are involved (6.7.7/6).

It's probably worth pointing out that    6.7.7/6 is a
non-normative comment.  Still, in the absense of anything else,
it is a good indication of what the authors' of the C standard
intended.

Note that in other contexts, even in C, typedef works like a
real type.  Consider    6.7.7/8: the size of the array is
evaluated at the typedef, and the symbol is bound once and for
all to the type with the size.  Logically, this would imply the
contrary of the example in    6.7.7/6 -- the type (signed int or
unsigned int) should be determined once and for all at the
moment the typedef is seen, and not be defered until it is used.
The example seems (to me at least) to contradict the (normative)
statement in    6.7.2/5: "[...] except that for bit-fields, it is
implementation-defined whether the specifier int designates the
same type as signed int or the same type as unsigned int."
Unless, of course, a typedef defines a synonym for a specifier,
and not for a type (but this would fail for the example in
   8.7.7/8).

> I can't find anything in the C++ standard that either
> explicitly agrees or disagrees but I'm going to go with what
> the C99 standard says here.  To see why this is a problem,
> take this code for instance:

> /// ty.h

> struct plain {
>  int a : 4;
> };

> template<class T>
> struct ty1 {
>  typedef T ty;
>  T a : 4;
> };

> /// foo.h

> #include <iostream>

> struct foo {
>  ty1<int>::ty a : 4;
> };

> inline void foo_fn()
> {
>  foo f;
>  f.a = -1;
> // implementation defined if it prints -1 or 15
>  std::cout << f.a;
> }

> See the problem?

In general, C++ has stricter typing that C.  In this case,
because of templates (but also things like the results of
typeid), a symbol defined by a typedef must define a type, not a
"specification".

> The standard says it is implementation defined whether
> plain::a is signed or unsigned (9.6/3), i.e. its range is
> either [-7,7] or [-8,7] (depending
> representation/padding/traps) or [0, 15]. For the sake of
> discussion, lets say our compiler makes plain bit-fields
> unsigned and that it uses 2s complement with no padding/traps
> for signed integers.

> ty1<int> is the same type as ty1<signed int> because signed
> int is the same as int, except in bit-fields, but uh-oh ty1
> contains a bit-field.  Taking what the C standard says
> (6.7.7/6) about plain typedefs (and naturally extending this
> to template parameters) into account, we see:

> plain::a's range is [0,15]
> ty1<int>::a's range is [0,15]
> ty1<signed int>::a's range is [-8,7]

> In order for ty1<int> to be ty1<signed int>, a's range has to
> be identical.

Another interesting question concerns the results of typeid on
the bitfield object.  Or what overload resolution or template
instantiation do if the object is used as a parameter?  Given:

    struct S
    {
        int b : 4 ;
    } ;

    void f( int ) ;
    void f( unsigned int ) ;

    S anS ;
    f( anS.b ) ;

Supposing the implementation described above (where plain int is
unsigned when declaring a bit field), which function f is
called?

--
James Kanze                                           GABI Software
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]