typedef a primitive type

Started by
18 comments, last by rnlf_in_space 10 years, 11 months ago

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.

Advertisement

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.

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

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”

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 (c)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

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...

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 ;-)

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.

throw table_exception("(? ???)? ? ???");

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.

throw table_exception("(? ???)? ? ???");

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';
}

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

This topic is closed to new replies.

Advertisement