Thread overview
typedef int: adding operators? (opAdd, etc.)
Apr 04, 2007
Charles D Hixson
Apr 04, 2007
BCS
Apr 04, 2007
Daniel Keep
Apr 04, 2007
Charles D Hixson
April 04, 2007
Is there any way to add operators defined on a type which is defined off of the built-in types?

In particular, it would be nice, e.g., to be able to define types for byteNo, wordNo, etc., and be able to add them together (where, say, one word == 4 bytes).

Currently I'm doing this kind of thing on an ad hoc basis, but that means that type information is spread out all over the place, and it would be nice to concentrate it in one place. (In one instance I'm converting between record ID's and disk addresses.  It's easy enough to do, but if I change the record size I'll need to track down each place where I cast from one type to another.  Not optimal.)

I suppose I could just write a special function for each conversion, and stick them all in the same place, and I may do just that, but this approach strikes me as less than ideal.

I suppose defining:
ByteNo opAdd(ByteNo b, WordNo w) { ... }
and then calling it via:
ByteNo b1 = opAdd(b0, w0);
isn't horrendous, but it's not as nice as
ByteNo b1 = b0 + w0;
April 04, 2007
Reply to Charles,

> Is there any way to add operators defined on a type which is defined
> off of the built-in types?
> 
> In particular, it would be nice, e.g., to be able to define types for
> byteNo, wordNo, etc., and be able to add them together (where, say,
> one word == 4 bytes).
> 
> Currently I'm doing this kind of thing on an ad hoc basis, but that
> means that type information is spread out all over the place, and it
> would be nice to concentrate it in one place. (In one instance I'm
> converting between record ID's and disk addresses.  It's easy enough
> to do, but if I change the record size I'll need to track down each
> place where I cast from one type to another.  Not optimal.)
> 
> I suppose I could just write a special function for each conversion,
> and stick them all in the same place, and I may do just that, but this
> approach strikes me as less than ideal.
> 
> I suppose defining:
> ByteNo opAdd(ByteNo b, WordNo w) { ... }
> and then calling it via:
> ByteNo b1 = opAdd(b0, w0);
> isn't horrendous, but it's not as nice as
> ByteNo b1 = b0 + w0;

somewhere there is a compile time units lib that does much of this kind of thing. It wraps the internal real type in a struct and forces the conventions to get to it. It might not be quite what you want because it would convert everything to the same type internally (bytes in your case) and you'd pay a price any time you use it as something else. You might take a look anyway.


April 04, 2007

BCS wrote:
> Reply to Charles,
> 
>> Is there any way to add operators defined on a type which is defined off of the built-in types?
>>
>> In particular, it would be nice, e.g., to be able to define types for byteNo, wordNo, etc., and be able to add them together (where, say, one word == 4 bytes).
>>
>> Currently I'm doing this kind of thing on an ad hoc basis, but that means that type information is spread out all over the place, and it would be nice to concentrate it in one place. (In one instance I'm converting between record ID's and disk addresses.  It's easy enough to do, but if I change the record size I'll need to track down each place where I cast from one type to another.  Not optimal.)
>>
>> I suppose I could just write a special function for each conversion, and stick them all in the same place, and I may do just that, but this approach strikes me as less than ideal.
>>
>> I suppose defining:
>> ByteNo opAdd(ByteNo b, WordNo w) { ... }
>> and then calling it via:
>> ByteNo b1 = opAdd(b0, w0);
>> isn't horrendous, but it's not as nice as
>> ByteNo b1 = b0 + w0;
> 
> somewhere there is a compile time units lib that does much of this kind of thing. It wraps the internal real type in a struct and forces the conventions to get to it. It might not be quite what you want because it would convert everything to the same type internally (bytes in your case) and you'd pay a price any time you use it as something else. You might take a look anyway.

I have a number of typedef'ed types, and I recently converted them all to structs.  The nice thing is that once they're structs you can do all the operator overloading you want (or omit operators that no longer make any sense), as well as add in conversion functions.

I sort of feel that typedefs, in their current form, are largely useless because no matter what you want the typedef for, structs seem to be a better choice.

	-- Daniel

-- 
int getRandomNumber()
{
    return 4; // chosen by fair dice roll.
              // guaranteed to be random.
}

http://xkcd.com/

v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP  http://hackerkey.com/
April 04, 2007
BCS wrote:
> Reply to Charles,
> 
>> Is there any way to add operators defined on a type which is defined
>> off of the built-in types?
>>
>> In particular, it would be nice, e.g., to be able to define types for
>> byteNo, wordNo, etc., and be able to add them together (where, say,
>> one word == 4 bytes).
>>
>> Currently I'm doing this kind of thing on an ad hoc basis, but that
>> means that type information is spread out all over the place, and it
>> would be nice to concentrate it in one place. (In one instance I'm
>> converting between record ID's and disk addresses.  It's easy enough
>> to do, but if I change the record size I'll need to track down each
>> place where I cast from one type to another.  Not optimal.)
>>
>> I suppose I could just write a special function for each conversion,
>> and stick them all in the same place, and I may do just that, but this
>> approach strikes me as less than ideal.
>>
>> I suppose defining:
>> ByteNo opAdd(ByteNo b, WordNo w) { ... }
>> and then calling it via:
>> ByteNo b1 = opAdd(b0, w0);
>> isn't horrendous, but it's not as nice as
>> ByteNo b1 = b0 + w0;
> 
> somewhere there is a compile time units lib that does much of this kind of thing. It wraps the internal real type in a struct and forces the conventions to get to it. It might not be quite what you want because it would convert everything to the same type internally (bytes in your case) and you'd pay a price any time you use it as something else. You might take a look anyway.
> 
> 
Sorry, that's a reasonable idea, but it's *NOT* what I want. This was just one example of a large number of cases.  It's because I have a tendency to use specialized types for error checking.  So I'll have (e.g.) an inches type, and a kilograms type, and then, LATER, I'll add a centimeters type.  O! but centimeters are smaller than inches...and they don't convert evenly.  So if I store the distances in centimeters, the result in inches will always be off...and everything I've stored previously will need to be reconverted.  (N.B.:  These are just examples.  Things I can use without explaining.)  But I don't want to do the conversions until they time when I need to.

And what I generally really want to do is either operate with a literal number (e.g., 5), or call a library routine without a lot of extraneous casts...which are each a point of failure if I later redesign some part.

(In the particular case in question I KNOW I'm going to redesign later.  One of the units is record # and another is disk address.  It currently depends on fixed length records, but it's going to be redesigned to accept records of varying length.  But not yet.  So I've got types like:
typeof uint RecordNo;
typeof uint DiskAddr;
Everything's fine, as long as I remember to put in all of the casts...but later things will get more complex, and already I've got expressions with casts of casts where I'm doing something like:
RecordNo newRec = cast(RecordNo)(1 + (cast(uint)diskLoc - headerSize ) / recSize);
That's not exactly one of them,..but it should give you a flavor.

A part of the reason that things are being done the way that they are is "It's a translation from C++".  I'm sure that there are better ways, and once I understand what I'm doing I'll figure some of them out.  For now, I'd just like the code to be clean enough not to cause problems later.  And I guess that means lots of functions like:
DiskAddress rn2da(RecordNo r) { ... }

Ugly, but better than lots of casts all over everywhere.  And easy to recognize when you're trying to figure out again "Just what's happening HERE?"