This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: hard typdef - proposal - I know it's not in the standard


On 1/24/13, Alec Teal <a.teal@warwick.ac.uk> wrote:
> Did anyone read?

I can sometimes take several days to get to reading an email,
particularly when travelling.

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

If you want your feature in mainline gcc, you will need to convince
the maintainers that the feature is valuable.  Likewise, if you want
your extension in the C++ language, you will need to convince the C++
standards committee.  Both tasks have essentially the same structure.

Clearly explain the programming problem you have.  You need to
explain why the current language is not really working.  Using
examples of real failures is helpful.  You should show that the
problem is significant to a significant number of programmers.
(They may not know they have a problem, so you will need to
explain it.)

The for your feature, IIUC, is that you are not proposing
something that keeps track of physical units, so many examples of
bad operations would not apply to your feature.  You need to make
the case that there is some aspect of programming that the feature
captures in code.

You need to examine a few alternative solutions.  Presumably,
your proposal is one of them.

Finally, you should discuss any interaction between your feature
and existing features.  In your case, you appear to be changing the
meaning of C casts.  What about C++ casts?  Do you need a new one,
or do you change the meaning of existing ones?

This list of tasks may seem like a lot of work, but it will likely be
significantly less than the implementation work.  More importantly,
it will help ensure that the feature has a market of users.

>
> 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.teal@warwick.ac.uk> wrote:
>
>>On 23/01/13 23:07, Lawrence Crowl wrote:
>>> On 1/23/13, Jonathan Wakely <jwakely.gcc@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"
>>
>>
>>
>


-- 
Lawrence Crowl


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]