Did anyone read? I hope you see how it is nothing like a strong typedef (as its called). To me it seems like the strong one is too similar to a class to be worth adding, especially after reading that paper, it seems like it would allow new-php-user like behaviour of EVERYTHING IS A CLASS but with types, we've all been there. While this could stop some errors ... I've discussed this already.
Alec I am eager to see what you guys think, this is a 'feature' I've wanted for a long time and you all seem approachable rather than the distant compiler gods I expected. I can also see why 'strong typedefs' were not done, it tries to do too much with the type system and becomes very object like.... Alec Teal <a.t...@warwick.ac.uk> wrote: >On 23/01/13 23:07, Lawrence Crowl wrote: >> On 1/23/13, Jonathan Wakely <jwakely....@gmail.com> wrote: >>> On 23 January 2013 09:15, Alec Teal wrote: >>>> I was fearful of using the word attribute for fear of getting it wrong? >>>> What >>>> is "this part" of the compiler called >>> I think attributes are handled in the front end and transformed into >>> something in the compiler's "tree" data structures. >>> >>> FWIW I've usually seen this feature referred to as "strong typedefs". >>> That brings to mind the "strong using" extension G++ had for >>> namespaces, which (prior to getting standardised as "inline >>> namespaces") used __attribute__((strong)) so that attribute already >>> exists in the C++ front end: >>> http://gcc.gnu.org/onlinedocs/gcc/Namespace-Association.html >> Note that there is a proposal before the C++ standard committee on >> just this topic. Consider its semantics when implementing. >> >> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3515.pdf >> >After reading that it doesn't seem like the same thing, they are talking >about - essentially - creating "classes" to handle types with no runtime >overhead, they want to be certain the optimizer got rid of it or save >the optimizer a job. I'm not saying that's bad I just think it is >separate. Typdefs are supposed to be an alias, yes in light of that the >title of "type definition" seems a little misleading when put with that >paper, but none the less a typedef is an alias. > >While reading the book "The design and evolution of C++" (I am not >saying this to throw around "look, from the 'founder' of C++'s mouth!", >I read it so I could learn why things are the way they are and errors >which would have happened if things had of happened differently, wow >that's a weird sentence, I mean failed attempts) I did enjoy reading >about the strict typing that C++ introduced and that was to catch errors. > >The paper has a point, you would never try to multiply two game scores, >so why have compiler tell you if you do? You'd surely mean it! What if >you want to computer the geometric mean (or something, go with me) >ultimately it's still an int! Why not go a step further and give the >compiler the concept of a group (algebraic structure see [1]) this could >stop divisions by zeros! Why not say you can no longer divide an integer >by another integer? MOST of the time the result wont be an integer, with >their example of cartesian(3) to spherical coordinates you shouldn't >have to rigidly define such stuff that it throws an error when you do >something as silly as mix them up and again, when they define x there is >no one-size-fits-all definition for how x operates with other types. If >you were to seriously try you'd just get so much spam, the only reason >the idea doesn't totally implode is because you can "cast" back to the >"base", so if all your definitions - which are more like conditions - >don't allow something, you just side-step it, this is an own goal the >point was you would never sidestep. > >There is a line between what I am proposing and what they are, I cannot >define exactly where it is, by their own admitance they cant: > >/"This issue has been one of the consistent stumbling blocks in the >design of an opaque typedef// >//facility. In particular, we have come to realize that there is no one >consistent approach to the// >//return type issue such that it will meet all expectations under all >circumstances//:"/ > >If they could this would be far more important than a proposed C++ >feature and would have been 'discovered' during the Abstract Algebra >boom during the times of Gauss. > > >Back on topic! >I always thought of a hard-typedef as an extension of this. I don't want >it treated like a class, I don't want this to be valid: > >---------------------------------------- >hard typdef int BookId; >int x = 5; >BookId my_book = x; //should fail >BookId alternate = (BookId) x; //fine - no runtime overhead because it's >just an alias, no compiling overhead really either. >---------------------------------------- > >This (in the world of classes) is interesting because if anything int is >the parent of BookId, but I shouldn't need a constructor or a >reinterpret_cast (however it'd be applicable) because I had written >"(BookId)" before the x, and that's probably not an accident the >compiler should realize I meant to do it. > >Now what about the other way? > >---------------------------------------- >hard typdef int BookId; >BookId x = 5; >int my_book = x; //should fail >int alternate = (int) x; //fine - no runtime overhead because LOOK! >---------------------------------------- > >Now in terms of inheritance we have it going both ways, so while you >could look at this as typdef system as "a set of classes all storing one >member of the same type with a different set of operations" you cannot >have implicit conversions both ways! It just makes no sense, yes it can >stop you writing stupid things anyway, but what use is there in defining >"denominator" to be nonzero or what about composite types I would never >want to multiply a real by an imaginary except inside the complex number >multiply function, what if I just want to deal with the imaginary part, >should I use their system and create this new imaginary type to be a >single number.... so forth, the document does talk about this and uses >the words "public" "private" and "protected" to talk about how to >restrict what can be done, just no, take this quote: > >"The programmer can similarly define a trampoline with = delete whenever a >particular combination of parameter types ought be disallowed." > >In their example they delete the * operation from their 'opaque typedef' >for "energy" > >energy something = (energy) ((double) e1* (double) e2); > >Just... no, that works around the thing they tried to do and looks awful! > > >That lack of one true way, the lack of "grand unified theory", and >ultimately don't forget GIGO, if you write stuff that swaps vector >components back it wont work. You can see how I struggle to define my >own line, C++'s stuff over C is not "one size fits all" but there's very >little specialization, where as the above allows for stupid amounts of it > >Back on topic! >Solution! No implicit 'casts'! >You could define implicit casts to be one way (up or down if you will) >or only with certain data-types to make writing stuff look a bit nicer >and involve less key-strokes but it can quickly become daft, one of the >other things I like about C++ is a quote from it's 'founder' "if it's an >ugly thing to do writing it should look ugly" or something to that >effect (talking about the various casts), so suppose you have this: > >Just generic db access stuff - how one might do it now (you could use a >struct, just go with it, this isn't about best practices in /that/ >sense), strings are database field names. >------------------------------------- >int book = cursor.get_int("BookId"); >int out_to = cursor.get_int("MemberId") >------------------------------------- > >This should not be allowed, ERROR by default, not warning, that's the >point: >------------------------------------- >hard typdef int BookId; >hard typdef int MemberId; >BookId book = cursor.get_int("BookId"); >MemberId member = cursor.get_int("MemberId"); >------------------------------------- >as it involves implicit casts, it looks nicer yes but it is still an int >to a BookId, also BookId looks much better in my IDE font, I've >developed my own styles over the years (working alone) please don't take >this as "CamelCase? This bitch isn't worth listening to, I know C(++) >people prefer _ but I'm so used to types being capital first... yeah, >I'll learn! Promise! > >This should be fine: >------------------------------------- >hard typdef int BookId; >hard typdef int MemberId; >BookId book = (BookId) cursor.get_int("BookId"); >MemberId member = (MemberId) cursor.get_int("MemberId"); >------------------------------------- > >and going the other way (this is pure pseudo-C++) >------------------------------------- >values.add("someInt",(int) someHardTypedefedTypeOfAnInt); >db.insert("table",values); >------------------------------------- > > > >*Note about ugliness: >*The point is that when you do this 'cast' you mean it, this stops you >from balsing stuff up, like using a MemberId where you mean BookId and >overloading (next thing ) you shouldn't have to type a lot though just >something to tell the compiler "I mean to write this". >I recently (and loved!) creating a scripting language for my work (mass >backspace, too far off topic even for me!) that had some static typing >(dynamic if unspecified) it wasn't really a speedup, it was very Lua >like in syntax but arrays started at 0, were not maps (there was a map) >and != was there instead of Lua's annoying ~=, anyway, rather than >writing "BookId x = (BookId) some_int;" I just wanted to tell the >compiler "I am aware that I am doing this" and I used "(~)" it parsed >this as a cast, I'd like to use this, so you could (if this were C++ >now) write: > > >------------------------------------- >hard typdef int BookId; >hard typdef int MemberId; >BookId book = (~) cursor.get_int("BookId"); >MemberId member = (~) cursor.get_int("MemberId"); >------------------------------------- > >I'll be honest, the main reason for this was my OCD powers of >doing-things-I-know-are-silly-or-else-cry so I could continue using tab >to align the equals signs without some variable length thing after. But >did it work or what! > >*Casts to things it's (the hard-typedef) not an alias of >*Well this is easy, the exact same way as the "base" type, keeping with >my BookId and MemberId as ints: > >float f = (~) some_book; //error, for so many reasons, (~) relies on the >relation of equality between hard-typedefs ONLY, there is no reason for >the compiler to 'replace' (~) with (int) or (MemberId) >//if anything the error should be generated from falling back to the >base type, will explain more shortly. > >float f = (float) some_book; //fine, you must intend a cast > >float f = (float) (~) some_book; //redundant, equiv to the above, but >still fine. - technically this is the correct way but as mentioned, a >cast is clearly intended in the above statement. > >as you know the float-int cast normally looks like this: >float f = (float) i; >HOWEVER there is no cast from a MemberId to a float, do what is >expected, be a normal typdef, you may think "Ah! Alec this voids your >own rule of implied types" but wait, sort of yes! But a cast is CLEARLY >intended. > >Now what about the other way? >int i = f; //f is some float >is truncating, we all know this, this is an implicit thing. > >consider two hard typedefs now, of length, area and volume (3 >real-valued units) this does seem kind of like the paper I'm not very >happy with and this type information should usually be carried in the >variable name but I can't think of any other examples, anyway: > >the trick here is to realise that it's not about being pedantic, length >l = 0.5; is unambiguous, this behavior will not always apply, remember >again that (~) is short hand for "any possible cast that'd work within >this group of hard-typedefs" > >length l = 5.0; //yes I know of the implicit cast from double to float >AND THEN to length, but it's a constant this is therefore fine, I could >formally define this but c'mon, that's just being a total pedant >(required later, but not now) >volume v = (~) l; //fine >volume v = (float) l; //NOT fine the point is volume is not a float >int k = v; //NOT FINE, we don't know that truncation makes sense on this >new type (see that line I talked about earlier?) only that it makes >sense for a float to be truncated >int k = (~) v; //fine >int k = (int) v; //fine because you clearly intend to cast. >int k = (int) (~) v; //fine, technically correct >int k = (int) (float) v; //what the above will "become" >int k = (int) (float) (length) (~) (~) (length) (float) (volume) (float) >v; //POINTLESS, but still valid, there is no 'correct' (~) as it just >means "one that exists that'd work here" > >*Is there a hierarchy of typedefs?* >obviously not, but it's worth thinking about why. > >hard typedef int BookId; >hard typedef BookId MemberId; >hard typedef int AutherId; >hard typedef RequestId BookId; > >Totally valid! > >int > +--BookId > | +--MemberId > | +--RequestId > +--AutherId > >Is the hierarchy, if any, but: >type(MemberId) = type(BookId) = type(int) >by the equivalence relation of equality this implies, if this is true then: >type(MemberId) = type(int) >so now: > >int > +--BookId > | +--RequestId > +--MemberId > +--AutherId > >is the same heirarchy, this is not true of class-inheritance btw incase >anyone is thinking "wait a minute" (inheritance is more like a<b<c so >a<c, it DOES distinguish between parent-child and sibling relationships!) >This does not distinguish between parent-child relationships (up/down >the hierarchy 'tree') and sibling relationships (same parent), infact >int isn't really the parent, it just happens to be the only one with >operations defined to allow it to interact with other types. > >This means that (~) can expand to any hard-typedef of it's incestuous >hierarchy (I don't have a word yet, group?) > >*If there exists a (~) it will be unique, you will never need to use two >casts directly after each other when 'traversing' a typedef group when >searching for a 'cast' to one known type*- can be proved formally >Works great for assignments, operator overloading (consider a definition >for whatever(int); whatever(MemberId); whatever(BookId); how do you pick >one if I say whatever((~)something);) > >again: >float f = (float) some_book; >is just shorthand for: >float f = (float) (int) some_book; >which is the same as: >float f = (float) (~) some_book; >as the cast is clearly intended (you WANT a float) you may use the first >one given, the point is to be sure you mean to, not to be annoying. > >*Overloading behavior >*EXACTLY as typedef BUT may be overloaded - unlike a typedef. > >that means: >hard typedef int ArrayFlag > >ArrayFlag APPEND = 0; >ArrayFlag START = 1; > >template<T> T& MyArray::operator[](ArrayFlag flag); >template<T> T& MyArray::operator[](unsigned int pos); //normal >array-like access. > >Now you can do: >MyArray<int> array; >array[APPEND] = 5; > >ALSO: >printNameOf(BookId); >printNameOf(MemberId); >.... > >define operators on these types (override adding two books, >whatever....) and use them as separate types resting assured there will >be no runtime performance drop! > >*Lastly >*(~) isn't very good if calling a function, like printNameOf((~) >(MemberId) 5); will throw an error for the same reason >MemberId x = 5; >printNameOf((~) x); >will throw an error, there is no unique type. No assumptions should be >made here (identity cast, (~) becomes (MemberId), not even that) > > > >That is the hard typedef! >* >* > > >Notes: >1) Group - in algebra a group is a structure consisting of a set and a >binary operation, let S be a set and @ be a binary operation, a group >has the following properties >* for an x,y in S x@y is also in s (closure) - implied by binary operation >* for an x in S there exists an i (identity element) that's also in S >such that x@i = i@x = x (existence of identity) >* for x,y,z in S x@(y@z) = (x@y)@z (associativity) >* for an x in S there exists a y in S such that x@y = i (the identity >element) (existence of an inverse) > >* for an Abelian (excuse my spelling, dyslexic :/) group for an x,y in S >x@y=y@x and is in S also (commutativity) > >Quick examples: > >Integers (signed :P) under addition, @ = + and S is >{...,-3,-2,-1,0,1,2,3,...} > >for integers x,y and z: >x+y is an integer >x+0 = 0+x = x, therefore 0 is the identity and that is an integer too. >x+(y+z) = (x+y)+z, addition is associative >x+(-x) = 0 so -x is the inverse of x, if x is an integer is -x? yes! so >the inverse is in S as required > >x+y=y+x (commutative) - note that this would not be the case with >subtraction, x-y != y-x > >2x2 matrices of real numbers (floats sort of) under multiplication: > >not a group because of the inverse property, if a matrix has a zero >determinant it has no inverse, a requirement. To be a group S would have >to be defined as "4 real numbers such that ad-bc != 0" > > >