Jump to content
  • Advertisement
Sign in to follow this  
Servant of the Lord

Unity A Proposal to Add Strong Type Aliases to the Standard Language

This topic is 2098 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey, I want strong type aliases in C++. I've never written a standard proposal before, so here's my attempt.

 

View PDF online

Download: Word document (20.7kb)

 

Any suggestions, additions, rewordings, and etc... you are able to offer would be much appreciated. After the community reviews and improves it, I'll email it to the standards committee (which are currently accepting proposals for C++ TR2), so it can hopefully be read and discussed at a committee meeting for addition to the C++ standard.

 

Please download the document, annote it in red, and re-post it here. Alternatively, post suggestions in the thread itself.

 

I would really appreciate it! smile.png

Aside from non-static data member initializers, which was added into C++11 (thank you, whoever submitted that proposal!), strong typedefs are one of my most desired C++ features.

Edited by Servant of the Lord

Share this post


Link to post
Share on other sites
Advertisement

Hm... Can I ask why this feature is vital? Conversion between

typedef unsigned int Centimeters;
typedef unsigned int Inches;

are legal as for me, as the real types are the same.

 

For incompatible types gcc (at least) drops an error:

??[santa@yukio ~ $]
?
??> g++ test.cpp -o test -pedantic -Wall
test.cpp: In function ‘int main()’:
test.cpp:9:8: error: cannot convert ‘MyFloat {aka float}’ to ‘MyInt {aka void*}’ for argument ‘1’ to ‘void test(MyInt)’
??[santa@yukio ~ $]
?
??> cat test.cpp 
typedef void* MyInt;
typedef float MyFloat;

void test(MyInt) {
}

int main() {
        MyFloat f;
        test(f);
        return 0;
}

For compatible types you won't get an error even without typedefs:

??[santa@yukio ~ $]
?
??> g++ test.cpp -o test -pedantic -Wall
test.cpp: In function ‘int main()’:
test.cpp:9:8: warning: ‘f’ is used uninitialized in this function [-Wuninitialized]
??[santa@yukio ~ $]
?
??> cat test.cpp 
void test(int) {
}

int main() {
        float f;
        test(f);
        return 0;
}

Share this post


Link to post
Share on other sites
Thanks SiCrane, I assumed there were prior proposals but didn't find them. I'll read over them.
Hm... Can I ask why this feature is vital? Conversion between
typedef unsigned int Centimeters;
typedef unsigned int Inches;
are legal as for me, as the real types are the same.
 
For incompatible types gcc (at least) drops an error: ...

For compatible types you won't get an error even without typedefs: ...
 



That's precisely the point. I don't want Centimeters and Inches to be convertible, except explicitly. Strong aliases should refuse to compile aliases made even from the same base type. You need a function to convert from Centimeters to Inches, because 1 Centimeter is not 1 Inch, and doing myCentimeters = myInches is easy to do and almost always a bug. Edited by Servant of the Lord

Share this post


Link to post
Share on other sites

[quote name='Servant of the Lord' timestamp='1356723633' post='5015148']
I don't want Centimeters and Inches to be convertible
[/quote]

Well and I don't understand why. As I wrote earlier built-in types with similar (in fact equal) semantics _are_ convertible. In your example you are trying to introduce a new type (not an alias in fact, alias is usually just another typename). Simple constructions such as `fancy_typedef old_type new_type' aren't able to describe the type semantics at all (both Centimeters and Inches are just lexems for compiler, you don't define what can be done to them). If you suggest semantics should be copied from the old_type than new_type would be just a typename, not a new type.

Share this post


Link to post
Share on other sites

I don't want Centimeters and Inches to be convertible

 
Well and I don't understand why. As I wrote earlier built-in types with similar (in fact equal) semantics _are_ convertible.



You seriously don't understand why it's desirable to get a compiler error if someone has a quantity in centimeters and tries to use it where a quantity in inches is expected? I am not sure how else to explain it, since SOTL has been very clear.

Share this post


Link to post
Share on other sites

[quote name='Álvaro' timestamp='1356725134' post='5015153']
You seriously don't understand why it's desirable to get a compiler error if someone has a quantity in centimeters and tries to use it where a quantity in inches is expected?
[/quote]

This is not my point. My point is thats not a compiler (c++ grammar) problem. Its worth implementing as a part of an STL.

Share this post


Link to post
Share on other sites

You seriously don't understand why it's desirable to get a compiler error if someone has a quantity in centimeters and tries to use it where a quantity in inches is expected?

 
This is not my point. My point is thats not a compiler (c++ grammar) problem. Its worth implementing as a part of an STL.



Well, it's a problem where the compiler could help, if it implemented what we are discussing.

I have felt the need for something like this when I have classes Vector3D and Point3D, which are essentially the same thing, but I need to define them separately if I want the type system to help me make sure my operations make sense (e.g., you are not allowed to add points, but adding vectors is fine, and so is adding a point and a vector).

Since we are dealing with units, I just wrote this little test that seems to work fine:
#include <iostream>

namespace units {
  template <int m_pow, int kg_pow, int s_pow>
  struct unit {
    double value;
    explicit unit(double value) : value(value) {
    }
  };
  
  template <int m_pow, int kg_pow, int s_pow>
  unit<m_pow,kg_pow,s_pow> operator+(unit<m_pow,kg_pow,s_pow> u1, unit<m_pow,kg_pow,s_pow> u2) {
    return unit<m_pow,kg_pow,s_pow>(u1.value+u2.value);
  }
  
  unit<0,0,0> operator+(unit<0,0,0> u, double d) {
    return unit<0,0,0>(u.value+d);
  }
  
  unit<0,0,0> operator+(double d, unit<0,0,0> u) {
    return unit<0,0,0>(d+u.value);
  }
  
  template <int m_pow, int kg_pow, int s_pow>
  unit<m_pow,kg_pow,s_pow> operator-(unit<m_pow,kg_pow,s_pow> u1, unit<m_pow,kg_pow,s_pow> u2) {
    return unit<m_pow,kg_pow,s_pow>(u1.value-u2.value);
  }

  unit<0,0,0> operator-(unit<0,0,0> u, double d) {
    return unit<0,0,0>(u.value-d);
  }

  unit<0,0,0> operator-(double d, unit<0,0,0> u) {
    return unit<0,0,0>(d-u.value);
  }

  template <int m_pow1, int kg_pow1, int s_pow1, int m_pow2, int kg_pow2, int s_pow2>
  unit<m_pow1+m_pow2,kg_pow1+kg_pow2,s_pow1+s_pow2> operator*(unit<m_pow1,kg_pow1,s_pow1> u1, unit<m_pow2,kg_pow2,s_pow2> u2) {
    return unit<m_pow1+m_pow2,kg_pow1+kg_pow2,s_pow1+s_pow2>(u1.value*u2.value);
  }

  template <int m_pow, int kg_pow, int s_pow>
  unit<m_pow,kg_pow,s_pow> operator*(unit<m_pow,kg_pow,s_pow> u, double d) {
    return unit<m_pow,kg_pow,s_pow>(u.value*d);
  }

  template <int m_pow, int kg_pow, int s_pow>
  unit<m_pow,kg_pow,s_pow> operator*(double d, unit<m_pow,kg_pow,s_pow> u) {
    return unit<m_pow,kg_pow,s_pow>(d*u.value);
  }

  template <int m_pow1, int kg_pow1, int s_pow1, int m_pow2, int kg_pow2, int s_pow2>
  unit<m_pow1-m_pow2,kg_pow1-kg_pow2,s_pow1-s_pow2> operator/(unit<m_pow1,kg_pow1,s_pow1> u1, unit<m_pow2,kg_pow2,s_pow2> u2) {
    return unit<m_pow1-m_pow2,kg_pow1-kg_pow2,s_pow1-s_pow2>(u1.value/u2.value);
  }

  template <int m_pow, int kg_pow, int s_pow>
  unit<m_pow,kg_pow,s_pow> operator/(unit<m_pow,kg_pow,s_pow> u, double d) {
    return unit<m_pow,kg_pow,s_pow>(u.value/d);
  }

  template <int m_pow, int kg_pow, int s_pow>
  unit<m_pow,kg_pow,s_pow> operator/(double d, unit<m_pow,kg_pow,s_pow> u) {
    return unit<-m_pow,-kg_pow,-s_pow>(d/u.value);
  }

  template <int m_pow, int kg_pow, int s_pow>
  std::ostream &operator<<(std::ostream &os, unit<m_pow,kg_pow,s_pow> u) {  
    os << u.value;
    if (m_pow != 0) {
      os << "m";
      if (m_pow != 1)
	os << "^" << m_pow;
    }
    if (kg_pow != 0) {
      os << "Kg";
      if (kg_pow != 1)
	os << "^" << kg_pow;
    }
    if (s_pow != 0) {
      os << "s";
      if (s_pow != 1)
	os << "^" << s_pow;
    }
    return os;
  }

  unit<1,0,0> meter(1);
  unit<1,0,0> centimeter(0.01);
  unit<1,0,0> inch(0.0254);
  unit<0,0,1> second(1.0);
  unit<0,0,1> minute(60.0);
  unit<0,0,1> hour(3600.0);
  unit<0,1,0> gram(0.001);
  // etc.
}

using namespace units;

int main() {
  std::cout << "100 inches/hour = " << (100.0*inch/hour)/(centimeter/minute) << " centimeters/minute\n";
}

Does anyone know if there is a C++ library that does this type of thing? (Oh, and sorry about the hijack...)

Share this post


Link to post
Share on other sites

It would be cool if you could define casting operators for opaque typedefs too*:

// Obviously this would require a few changes to the Standard
Inches operator Centimeters() (Centimeters cm)
{
    // Estimate
    return (cm * 5) / 2;
    // Note that the above is merely example code; I realize that a) the above isn't necessarily the
    // the right data type, and b) casting it to the right data type could cause recursion without some
    // changes or facilities added to the Standard.
}
 
inline constexpr Centimeters operator"" _cm (int cm)
{
    return (Centimeters)cm;
}
 
Inches i = (Inches)(12_cm); // The point of this code is to show this line

 

Also, what would be the implications with promotions? What if you multiplied Inches (which is typedefd as an int) by a float? Or multiplied it by an int?

 

*This is, I would say, related to opaque typedefs, but is also a bit of a separate issue (because declaring a casting operator makes implicit conversions possible, and AFAIK you can't make a casting operator that requires explicit casting). I suppose you could also say it could work for non-opaque typedefs/implicit conversions, but I don't like the ambiguity of Inches i = 12_cm;

Edited by Cornstalks

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!