Jump to content

  • Log In with Google      Sign In   
  • Create Account


typedef a primitive type


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
20 replies to this topic

#1 lride   Members   -  Reputation: 633

Like
0Likes
Like

Posted 21 May 2013 - 08:19 PM

I often see typedefs on a primitive type. 

For example

    typedef signed   char int8;
    typedef unsigned char uint8;

    typedef signed   short int16;
    typedef unsigned short uint16;

 

What's the point of this?


An invisible text.

Sponsor:

#2 Cornstalks   Crossbones+   -  Reputation: 6974

Like
10Likes
Like

Posted 21 May 2013 - 08:40 PM

In C++, the size (in bits) of a data type is not guaranteed. Sometimes though, you need to ensure a specific size for your data types (this happens a lot when working with bit streams). These types might be typedefed so that on whatever compiler/platform the code is built on, when you need an 8-bit signed integer, you really do get an 8-bit signed integer.

 

Say, for example, you need a 16-bit signed integer. On one system, short might be 16 bits, and on another system, short might be 32 bits. With typedefs, it allows you to simply use int16 in your code, and then if you switch between these two different systems where short is two different sizes, you can just change the typedef to the appropriate type and your code should work just fine (rather than having to hunt down all the places in your code and make changes all over the place just to get a 16 bit integer).

 

Sometimes it's not so you can switch between one system and another (like in the previous example). Sometimes it's to explicitly state that you require a data type with a very specific size, and if the system does not support that size, then your code does not support that system. Saying int8 makes it very clear you require an 8 bit integer.

 

Of course, the typedef itself doesn't enforce the requirement (it doesn't care how big your data types are or what they're named), but using an appropriately named type in your code makes it very clear what you require. You can then, if you want, force the compilation to fail if the requirements are not met (for example, you can fail the compilation if int8 is not 8 bits, or if int16 is not 16 bits), in addition to making the requirements in the code self-documenting (because int8 "documents" the code as requiring an 8-bit signed integer, whereas just saying signed char does not "document" the code as requiring an 8-bit signed integer).


Edited by Cornstalks, 21 May 2013 - 08:43 PM.

[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#3 rnlf   Members   -  Reputation: 1116

Like
5Likes
Like

Posted 22 May 2013 - 01:23 AM

Just as an addition, I always prefer using the C99 header stdint.h (also available as cstdint in C++) and the types uint8_t and similar and not handling this mess myself. In one company I worked for, this was not done and during a switch to 64 bit I had to update a whole bunch of header files with lots of #ifdefs to take care of this. Had the project used stdint, it would have saved me a whole day of debugging to find the problem and fix it.

Edited by rnlf, 22 May 2013 - 01:25 AM.

my blog (German)


#4 Álvaro   Crossbones+   -  Reputation: 12372

Like
3Likes
Like

Posted 22 May 2013 - 08:30 AM

Just as an addition, I always prefer using the C99 header stdint.h (also available as cstdint in C++) and the types uint8_t and similar and not handling this mess myself. In one company I worked for, this was not done and during a switch to 64 bit I had to update a whole bunch of header files with lots of #ifdefs to take care of this. Had the project used stdint, it would have saved me a whole day of debugging to find the problem and fix it.

 

I agree that stdint.h should be used, but before that was available I used to write a little program that would check the size of various integer types (using sizeof) and produce the text of a header file with those typedefs. The makefile knew to compile and run this program to generate the header file. I used that without problems for years.

 

 

#include <iostream>
#include <string>
#include <climits>
#include <cstdlib>

std::string find_type(int size) {
  if (CHAR_BIT * sizeof(char) == size) return "char";
  if (CHAR_BIT * sizeof(short) == size) return "short";
  if (CHAR_BIT * sizeof(int) == size) return "int";
  if (CHAR_BIT * sizeof(long) == size) return "long";
  if (CHAR_BIT * sizeof(long long) == size) return "long long"; // OK in gcc
  std::cerr << "ERROR: I couldn't find a " << size << "-bit type!\n";
  std::exit(1);
}

void define_signed_and_unsigned(int size) {
  std::cout << "typedef signed " << find_type(size) << " int" << size << ";\n";
  std::cout << "typedef unsigned " << find_type(size) << " uint" << size << ";\n";
}

int main() {
  define_signed_and_unsigned(8);
  define_signed_and_unsigned(16);
  define_signed_and_unsigned(32);
  define_signed_and_unsigned(64);
}
 

Edited by Álvaro, 22 May 2013 - 08:40 AM.


#5 Ravyne   Crossbones+   -  Reputation: 6904

Like
2Likes
Like

Posted 22 May 2013 - 01:51 PM

In this case, others have already explained the specific use here, but more generally typedefs of primitive (and non-primitive) types can be used to provide additional context about their use.

 

Primitive types convey only two things: Their size (which is platform specific), and their format (which is also platform specific, though less obviously so). But it doesn't convey a purpose or intent as to what it holds. For example, 'typedef float velocity;' and 'typedef float acceleration;' give you added information about what instances of these types (ought to) hold, that would otherwise be described only by the variable name. Now, perhaps unfortunately, typedefs aren't strong -- that is, they don't create new distinct types, they just allow you to call a type by another name -- so you can still assign a 'velocity' variable to an 'acceleration' variable or a plain old float. But the point is that it creates a logical distinction between them, even if its not enforced by the compiler.

 

Another practical advantage is that if you decide that float is insufficient for your 'velocity' or 'acceleration' types, then you can easily redefine them to be of type 'double' in only a single place, rather than hunting through every single float in your code to determine whether it holds a velocity or acceleration. In this way it can save you time and avoid subtle bugs that arise from missing one of the things you should have changed.



#6 lride   Members   -  Reputation: 633

Like
0Likes
Like

Posted 22 May 2013 - 05:00 PM

Thank you all. I now understand it.

So a typedef header cannot be cross platform. Am I right?


An invisible text.

#7 L. Spiro   Crossbones+   -  Reputation: 12754

Like
0Likes
Like

Posted 22 May 2013 - 05:15 PM

What is a “typedef header”?  A header full of typedef’s?

 

In any case, yes, they can be cross-platform.  That is the point.

 

 

L. Spiro


It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#8 Ravyne   Crossbones+   -  Reputation: 6904

Like
0Likes
Like

Posted 22 May 2013 - 05:36 PM

Primitive types are *not* cross-platform in terms of the number of bits they contain or their representation -- IIRC, paraphrasing the standard, it says only something along the lines of "a 'char' is the smallest addressable unit of storage; a 'short' is at least as big as a 'char'; an 'int' is at least as big as a 'short'; ..." The standard doesn't say that an 'int' is exactly 32 bits (although it is in many platforms), I don't even believe it says that signed numbers must be represented as two's compliment form.

 

The idea of a typedef header like stdint.h is to define a type (the typedef) which *is* the same number of bits across many platforms, by changing the underlying primitive types appropriately on a platform-specific basis. In other words, the idea is that uint32_t is always a 32bit unsigned integer on any platform, but the underlying primitive type might be different on a 32bit x86 machine running Windows than it is on a 64bit MIPS machine running Unix.


Edited by Ravyne, 22 May 2013 - 05:37 PM.


#9 Hodgman   Moderators   -  Reputation: 28438

Like
2Likes
Like

Posted 22 May 2013 - 06:58 PM

Thank you all. I now understand it.

So a typedef header cannot be cross platform. Am I right?

You make these headers be cross platform like:

#if defined(PLATFORM_ONE)
typedef foo int32;
#elif defined(PLATFORM_TWO)
typedef bar int32;
#else
#error "this platform not supported"'
#endif


#10 lride   Members   -  Reputation: 633

Like
0Likes
Like

Posted 22 May 2013 - 07:48 PM

Thank you all. I now understand it.

So a typedef header cannot be cross platform. Am I right?

You make these headers be cross platform like:

 

#if defined(PLATFORM_ONE)
typedef foo int32;
#elif defined(PLATFORM_TWO)
typedef bar int32;
#else
#error "this platform not supported"'
#endif

Oh I see, I didn't think of conditional compilation


An invisible text.

#11 Dragonsoulj   Crossbones+   -  Reputation: 2015

Like
0Likes
Like

Posted 23 May 2013 - 01:43 AM

For Hodgeman's point: http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system

 

Nice list of the various defines.



#12 rnlf   Members   -  Reputation: 1116

Like
0Likes
Like

Posted 23 May 2013 - 02:00 AM

Now, perhaps unfortunately, typedefs aren't strong


Ada does this. It's a nice safety rope but it can also get quite messy. Velocity equals acceleration times time? Nope, not without converting everything to a common base type. It is nice to know that you cannot mix different types by accident, but it also means you cannot mix them intentionally without writing more code.

Edited by rnlf, 23 May 2013 - 02:01 AM.

my blog (German)


#13 Bacterius   Crossbones+   -  Reputation: 8278

Like
0Likes
Like

Posted 23 May 2013 - 04:08 AM

Ada does this. It's a nice safety rope but it can also get quite messy. Velocity equals acceleration times time? Nope, not without converting everything to a common base type. It is nice to know that you cannot mix different types by accident, but it also means you cannot mix them intentionally without writing more code.

 

Now I want to see a language that associates units with variables and intelligently checks that they remain valid throughout statements huh.png


The slowsort algorithm is a perfect illustration of the multiply and surrender paradigm, which is perhaps the single most important paradigm in the development of reluctant algorithms. The basic multiply and surrender strategy consists in replacing the problem at hand by two or more subproblems, each slightly simpler than the original, and continue multiplying subproblems and subsubproblems recursively in this fashion as long as possible. At some point the subproblems will all become so simple that their solution can no longer be postponed, and we will have to surrender. Experience shows that, in most cases, by the time this point is reached the total work will be substantially higher than what could have been wasted by a more direct approach.

 

- Pessimal Algorithms and Simplexity Analysis


#14 Trienco   Crossbones+   -  Reputation: 2077

Like
0Likes
Like

Posted 23 May 2013 - 05:16 AM

And if you don't mind that it will probably your compile times and might have issues with compilers where certain types don't exist, you can use black template magic to get around ifdefs and having to know the different platforms and type sizes in advance.

 

Essentially you simply stuff all signed, unsigned and floating point types into a type list and let the template recursively go through this list until it finds one with the requested sizeof. Though this is only going to be of any help if a) you can't use the ©stdint header and b) really don't want to worry about all the potential platforms. It's also going to be messy on exotic machines, where one "byte" can be more than 8bit.

 

 
template<bool condition, class A, class B> struct SelectT { typedef A Type; };
template<class A, class B> struct SelectT<false, A, B> { typedef B Type; };
 
struct NullType;
 
template<typename A = NullType, typename B = NullType, typename C = NullType,
typename D = NullType, typename E = NullType, typename F = NullType>
struct TypeList
{
typedef A Head;
typedef TypeList<B,C,D,E,F> Tail;
};
 
template<size_t size, typename list>
struct TypeOfSize
{
typedef typename SelectT
< sizeof(typename list::Head)==size,
 typename list::Head,
 typename TypeOfSize<size, typename list::Tail>::type
>::Type type;
};
 
template<size_t size> struct TypeOfSize<size, TypeList<> > { typedef NullType type; };
 
 
 
typedef TypeList<char, short, int, long, long long> SignedType;
typedef TypeList<unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long> UnsignedType;
typedef TypeList<float, double, long double> FloatingType;
 
typedef TypeOfSize<1, SignedType>::type int8;
typedef TypeOfSize<2, SignedType>::type int16;
typedef TypeOfSize<4, SignedType>::type int32;
typedef TypeOfSize<8, SignedType>::type int64;
 
typedef TypeOfSize<1, UnsignedType>::type uint8;
typedef TypeOfSize<2, UnsignedType>::type uint16;
typedef TypeOfSize<4, UnsignedType>::type uint32;
typedef TypeOfSize<8, UnsignedType>::type uint64;
 
typedef TypeOfSize<1, FloatingType>::type float8;
typedef TypeOfSize<2, FloatingType>::type float16;
typedef TypeOfSize<4, FloatingType>::type float32;
typedef TypeOfSize<8, FloatingType>::type float64;

f@dzhttp://festini.device-zero.de

#15 Álvaro   Crossbones+   -  Reputation: 12372

Like
3Likes
Like

Posted 23 May 2013 - 06:13 AM

Bacterius, on 23 May 2013 - 06:12, said:

rnlf, on 23 May 2013 - 04:04, said:
Ada does this. It's a nice safety rope but it can also get quite messy. Velocity equals acceleration times time? Nope, not without converting everything to a common base type. It is nice to know that you cannot mix different types by accident, but it also means you cannot mix them intentionally without writing more code.

Now I want to see a language that associates units with variables and intelligently checks that they remain valid throughout statements huh.png


C++ can do that. You can use a template class that has integer parameters indicating the power of kilogram, meter and second. All the class does is wrap a floating-point number. Now define operators that deal with types appropriately.

Or you can use Boost.Units.

EDIT: Fixed the link. This editor and I don't get along...

Edited by Álvaro, 23 May 2013 - 08:34 AM.


#16 rnlf   Members   -  Reputation: 1116

Like
0Likes
Like

Posted 23 May 2013 - 08:11 AM

Uuhh... nice idea. Haven't heard of this one before, nor of Boost.Units. I will definitely look into this.

 

edit: Your link is broken, but still intelligible ;-)


Edited by rnlf, 23 May 2013 - 08:13 AM.

my blog (German)


#17 Ravyne   Crossbones+   -  Reputation: 6904

Like
1Likes
Like

Posted 23 May 2013 - 01:39 PM

Ada does this. It's a nice safety rope but it can also get quite messy. Velocity equals acceleration times time? Nope, not without converting everything to a common base type. It is nice to know that you cannot mix different types by accident, but it also means you cannot mix them intentionally without writing more code.

 

Now I want to see a language that associates units with variables and intelligently checks that they remain valid throughout statements huh.png

 

As others have said, its possible to put together a pretty good system in C++ using templates, but some languages do have this kind of notion built in. Haskell and, I think, F# are two examples. I believe its called type annotation or somesuch.



#18 Ravyne   Crossbones+   -  Reputation: 6904

Like
0Likes
Like

Posted 23 May 2013 - 01:52 PM

Now, perhaps unfortunately, typedefs aren't strong


Ada does this. It's a nice safety rope but it can also get quite messy. Velocity equals acceleration times time? Nope, not without converting everything to a common base type. It is nice to know that you cannot mix different types by accident, but it also means you cannot mix them intentionally without writing more code.

 

Yeah, it does take effort to define the proper relationships, and it can grow into a sort of combinatorial problem. The approach would be to overload operators and/or create classes that represent various units. AFAIK, this is basically what the C++ template libraries do, except that they use template magic to generate the code, perhaps type traits as well in their implementation.

 

Its a mixed bag whether strong or weak typedefs are preferable, personally I think it would have been nice to have both options: typedef as strong, typealias as weak. C/C++ typedefs are weak mostly because C's notion of type equality is rooted in its physical properties (size and format), rather than its logical ones (its intent). That's a perfectly reasonable choice for a systems language, but its unfortunate that C++ didn't introduce a way to make a new strong type other than classes.



#19 Álvaro   Crossbones+   -  Reputation: 12372

Like
5Likes
Like

Posted 23 May 2013 - 05:21 PM

Here's some straight-forward code that implements units combining kilograms, meters and seconds:

#include <iostream>
 
template <int kp, int mp, int sp>
class Unit {
  double x;
  
public:
  Unit(double x) : x(x) {
  }
  
  double as_double() const {
    return x;
  }
};
 
template <int kp, int mp, int sp>
std::ostream &operator<<(std::ostream &os, Unit<kp,mp,sp> u) {
  os << u.as_double();
  if (kp) {
    os << " Kg";
    if (kp != 1)
      os << "^" << kp;
  }
  if (mp) {
    os << " m";
    if (mp != 1)
      os << "^" << mp;
  }
  if (sp) {
    os << " s";
    if (sp != 1)
      os << "^" << sp;
  }
  
  return os;
}
 
template <int kp, int mp, int sp>
Unit<kp,mp,sp> operator+(Unit<kp,mp,sp> lhs, Unit<kp,mp,sp> rhs) {
  return Unit<kp,mp,sp>(lhs.as_double() + rhs.as_double());
}
 
template <int kp, int mp, int sp>
Unit<kp,mp,sp> operator-(Unit<kp,mp,sp> lhs, Unit<kp,mp,sp> rhs) {
  return Unit<kp,mp,sp>(lhs.as_double() - rhs.as_double());
}
 
template <int kp, int mp, int sp>
Unit<kp,mp,sp> operator-(Unit<kp,mp,sp> u) {
  return Unit<kp,mp,sp>(-u.as_double());
}
 
template <int kp, int mp, int sp>
Unit<kp,mp,sp> operator*(double s, Unit<kp,mp,sp> u) {
  return Unit<kp,mp,sp>(s * u.as_double());
}
 
template <int kp, int mp, int sp>
Unit<kp,mp,sp> operator*(Unit<kp,mp,sp> u, double s) {
  return Unit<kp,mp,sp>(u.as_double() * s);
}
 
template <int kp1, int mp1, int sp1, int kp2, int mp2, int sp2>
Unit<kp1+kp2,mp1+mp2,sp1+sp2> operator*(Unit<kp1,mp1,sp1> lhs, Unit<kp2,mp2,sp2> rhs) {
  return Unit<kp1+kp2,mp1+mp2,sp1+sp2>(lhs.as_double() * rhs.as_double());
}
 
template <int kp1, int mp1, int sp1, int kp2, int mp2, int sp2>
Unit<kp1-kp2,mp1-mp2,sp1-sp2> operator/(Unit<kp1,mp1,sp1> lhs, Unit<kp2,mp2,sp2> rhs) {
  return Unit<kp1-kp2,mp1-mp2,sp1-sp2>(lhs.as_double() / rhs.as_double());
}
 
typedef Unit<1,0,0> Mass;
typedef Unit<0,1,0> Length;
typedef Unit<0,0,1> Time;
typedef Unit<0,1,-1> Speed;
typedef Unit<0,1,-2> Acceleration;
typedef Unit<1,1,-2> Force;
typedef Unit<1,2,-2> Energy;
typedef Unit<1,2,-3> Power;
 
Mass const Kg(1.0);
Length const m(1.0);
Time const s(1.0);
Force const N(1.0);
Energy const J(1.0);
Power const W(1.0);
 
int main() {
  Acceleration g = 9.8*m/(s*s);
  Length height = 10.0*m;
  Mass mass = 1.0*Kg;
  Speed speed = 45*m/s;
  Energy potential_energy = mass * g * height;
  Energy kinetic_energy = 0.5 * mass * speed * speed;
  Energy total_energy = potential_energy + kinetic_energy;
  std::cout << total_energy << '\n';
}


#20 rnlf   Members   -  Reputation: 1116

Like
0Likes
Like

Posted 24 May 2013 - 01:37 AM

I actually like this a lot. I am using C++ for more than 10 years now and I'm still amazed by its expressiveness.


my blog (German)





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS