www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is there a way to remove the requirement for parenthesis?

reply Charles Hixson <charleshixsn earthlink.net> writes:
In this test I'm trying to emulate how I want a typedef to act, but I 
run into a problem:

import   std.stdio;

struct   BlockNum
{  uint   value;

    uint   opCast()   {   return   value;   }
    void   opAssign (uint val)   {   value = val;   }
    uint   opCall()   {   return   value;   }
}

void   main()
{  BlockNum   test;
    test   =   42;
    uint   tst2   =   test();  // <<== if I don't have the parenthesis I
                               //    get a compiler error (cast
                               //    required).
           //  kfile.d(15): Error: cannot implicitly convert expression
           //          (test) of type BlockNum to uint

    writef ("tst2 = %d\n", tst2);
}

It seemed to me as if the parens shouldn't be required here, but I seem 
mistaken.  Which leads to ugly code.  Is there a way around this?
Jan 21 2009
next sibling parent reply Charles Hixson <charleshixsn earthlink.net> writes:
P.S.:  This is Digital Mars D Compiler v2.023 running on Linux

Charles Hixson wrote:
 In this test I'm trying to emulate how I want a typedef to act, but I 
 run into a problem:
 
 import   std.stdio;
 
 struct   BlockNum
 {  uint   value;
 
    uint   opCast()   {   return   value;   }
    void   opAssign (uint val)   {   value = val;   }
    uint   opCall()   {   return   value;   }
 }
 
 void   main()
 {  BlockNum   test;
    test   =   42;
    uint   tst2   =   test();  // <<== if I don't have the parenthesis I
                               //    get a compiler error (cast
                               //    required).
           //  kfile.d(15): Error: cannot implicitly convert expression
           //          (test) of type BlockNum to uint
 
    writef ("tst2 = %d\n", tst2);
 }
 
 It seemed to me as if the parens shouldn't be required here, but I seem 
 mistaken.  Which leads to ugly code.  Is there a way around this?
Jan 21 2009
parent "Denis Koroskin" <2korden gmail.com> writes:
On Wed, 21 Jan 2009 20:26:08 +0300, Charles Hixson <charleshixsn earthlink.net>
wrote:

 P.S.:  This is Digital Mars D Compiler v2.023 running on Linux

 Charles Hixson wrote:
 In this test I'm trying to emulate how I want a typedef to act, but I  
 run into a problem:
  import   std.stdio;
  struct   BlockNum
 {  uint   value;
     uint   opCast()   {   return   value;   }
    void   opAssign (uint val)   {   value = val;   }
    uint   opCall()   {   return   value;   }
 }
  void   main()
 {  BlockNum   test;
    test   =   42;
    uint   tst2   =   test();  // <<== if I don't have the parenthesis I
                               //    get a compiler error (cast
                               //    required).
           //  kfile.d(15): Error: cannot implicitly convert expression
           //          (test) of type BlockNum to uint
     writef ("tst2 = %d\n", tst2);
 }
  It seemed to me as if the parens shouldn't be required here, but I  
 seem mistaken.  Which leads to ugly code.  Is there a way around this?
No, there isn't. It leads to ambiguity and here is why: class Foo { Foo opCall() { return new Foo(); } } void main() { Foo bar = new Foo(); auto ambiguous = bar; // is it 'bar' or 'bar()'? } One more case where empty pair of parens is mandatory. To Walter & Co: Please, oh *PLEASE* drop this feature and give us real properties!
Jan 21 2009
prev sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Wed, 21 Jan 2009 09:24:01 -0800, Charles Hixson wrote:

 In this test I'm trying to emulate how I want a typedef to act, but I 
 run into a problem:
 
 import   std.stdio;
 
 struct   BlockNum
 {  uint   value;
 
     uint   opCast()   {   return   value;   }
     void   opAssign (uint val)   {   value = val;   }
     uint   opCall()   {   return   value;   }
 }
 
 void   main()
 {  BlockNum   test;
     test   =   42;
     uint   tst2   =   test();  // <<== if I don't have the parenthesis I
                                //    get a compiler error (cast
                                //    required).
            //  kfile.d(15): Error: cannot implicitly convert expression
            //          (test) of type BlockNum to uint
 
     writef ("tst2 = %d\n", tst2);
 }
 
 It seemed to me as if the parens shouldn't be required here, but I seem 
 mistaken.  Which leads to ugly code.  Is there a way around this?
test is an expression of type BlockNum. opCall() is called when you use parentheses syntax on it. opCast() is called when you use cast() syntax for it. Otherwise it stays BlockNum and therefore is not convertible to uint.
Jan 25 2009
parent reply Charles Hixson <charleshixsn earthlink.net> writes:
Sergey Gromov wrote:
 Wed, 21 Jan 2009 09:24:01 -0800, Charles Hixson wrote:
 
 In this test I'm trying to emulate how I want a typedef to act, but I 
 run into a problem:

 import   std.stdio;

 struct   BlockNum
 {  uint   value;

     uint   opCast()   {   return   value;   }
     void   opAssign (uint val)   {   value = val;   }
     uint   opCall()   {   return   value;   }
 }

 void   main()
 {  BlockNum   test;
     test   =   42;
     uint   tst2   =   test();  // <<== if I don't have the parenthesis I
                                //    get a compiler error (cast
                                //    required).
            //  kfile.d(15): Error: cannot implicitly convert expression
            //          (test) of type BlockNum to uint

     writef ("tst2 = %d\n", tst2);
 }

 It seemed to me as if the parens shouldn't be required here, but I seem 
 mistaken.  Which leads to ugly code.  Is there a way around this?
test is an expression of type BlockNum. opCall() is called when you use parentheses syntax on it. opCast() is called when you use cast() syntax for it. Otherwise it stays BlockNum and therefore is not convertible to uint.
I think that means "No, there isn't a way around it." OK. I'll just ... well, I'm not totally sure. Either give up type safety or ... something else.
Jan 27 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Honestly, I can't see what you're trying to accomplish.  It looks like
you want something that's not called int, but which works exactly like
an int does, and can be passed as one.

If you just want another name for "int", you can use an alias.

From the compiler's POV, there's no difference between "BlockNum" and "int".

 alias int BlockNum;
 BlockNum a = 42;
 someFuncThatTakesAnInt(a);
If you want to have a type that is an int, but which won't allow itself to directly interact with ints, use a typedef. Personally, I like this usage for simple numeric values which I don't want to accidentally mix with other types. Yes, it's a bit of a pain to do arithmetic, but that's the trade-off you make. From the compiler's POV, "BlockNum" and "int" are totally distinct, incompatible types that just happen to be the same under the hood.
 typedef int BlockNum;
 BlockNum a = cast(BlockNum) 42;
 someFuncThatTakesAnInt(cast(int) a);
The last is if you need something that's basically an int, but you want it to behave differently. In that case, a struct with operators is your best bet. Let's say you wanted to do something like a Meters struct to store lengths. I'd do something like this:
 struct Meters {
     private int value;
     int asInt() { return value; }
     int asInt(int v) { return value=v; }
     // ... operator overloads ...
 }

 Meters a; a.asInt = 42;
 someFuncThatTakesAnInt( a.asInt );
I can't really offer more than that, since I don't know what it is you're trying to accomplish. -- Daniel
Jan 27 2009
next sibling parent Charles Hixson <charleshixsn earthlink.net> writes:
I guess I'm going to do that, but it causes an annoying proliferation of 
casts throughout the program whenever I need to interact with a library 
call that recognized more than one int type.  (The compiler, D2.023 on 
Linux) doesn't seem to properly upcast the type.

Perhaps I'll write separate functions through which to interact with 
library routines.  Unpleasant and messy, but better than scattering 
casts all over the code (as long as they get optimized away).

Daniel Keep wrote:
 Honestly, I can't see what you're trying to accomplish.  It looks like
 you want something that's not called int, but which works exactly like
 an int does, and can be passed as one.
 
 If you just want another name for "int", you can use an alias.
 
 From the compiler's POV, there's no difference between "BlockNum" and "int".
 
 alias int BlockNum;
 BlockNum a = 42;
 someFuncThatTakesAnInt(a);
If you want to have a type that is an int, but which won't allow itself to directly interact with ints, use a typedef. Personally, I like this usage for simple numeric values which I don't want to accidentally mix with other types. Yes, it's a bit of a pain to do arithmetic, but that's the trade-off you make. From the compiler's POV, "BlockNum" and "int" are totally distinct, incompatible types that just happen to be the same under the hood.
 typedef int BlockNum;
 BlockNum a = cast(BlockNum) 42;
 someFuncThatTakesAnInt(cast(int) a);
The last is if you need something that's basically an int, but you want it to behave differently. In that case, a struct with operators is your best bet. Let's say you wanted to do something like a Meters struct to store lengths. I'd do something like this:
 struct Meters {
     private int value;
     int asInt() { return value; }
     int asInt(int v) { return value=v; }
     // ... operator overloads ...
 }

 Meters a; a.asInt = 42;
 someFuncThatTakesAnInt( a.asInt );
I can't really offer more than that, since I don't know what it is you're trying to accomplish. -- Daniel
Jan 27 2009
prev sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Wed, 28 Jan 2009 08:45:12 +0300, Daniel Keep <daniel.keep.lists gmail.com>
wrote:

 Honestly, I can't see what you're trying to accomplish.  It looks like
 you want something that's not called int, but which works exactly like
 an int does, and can be passed as one.

 If you just want another name for "int", you can use an alias.

 From the compiler's POV, there's no difference between "BlockNum" and  
 "int".

 alias int BlockNum;
 BlockNum a = 42;
 someFuncThatTakesAnInt(a);
If you want to have a type that is an int, but which won't allow itself to directly interact with ints, use a typedef. Personally, I like this usage for simple numeric values which I don't want to accidentally mix with other types. Yes, it's a bit of a pain to do arithmetic, but that's the trade-off you make. From the compiler's POV, "BlockNum" and "int" are totally distinct, incompatible types that just happen to be the same under the hood.
 typedef int BlockNum;
 BlockNum a = cast(BlockNum) 42;
 someFuncThatTakesAnInt(cast(int) a);
The last is if you need something that's basically an int, but you want it to behave differently. In that case, a struct with operators is your best bet. Let's say you wanted to do something like a Meters struct to store lengths. I'd do something like this:
 struct Meters {
     private int value;
     int asInt() { return value; }
     int asInt(int v) { return value=v; }
     // ... operator overloads ...
 }

 Meters a; a.asInt = 42;
 someFuncThatTakesAnInt( a.asInt );
I can't really offer more than that, since I don't know what it is you're trying to accomplish. -- Daniel
Perhaps, he wants opImplicitCast?
Jan 28 2009
parent reply Charles Hixson <charleshixsn earthlink.net> writes:
Suppose that you have four types, equivalent to, say, float.
Call one of them Horiz, one Vertic, one Radians, and one Radius.
These are all floats, but when you specify, say,
float dist (Horiz x, Vert y)
{  return sqrt(x * x + y * y);  }
It's important that the arguments aren't Radius and Radians.  Or Horiz 
and Horiz.

Denis Koroskin wrote:
 On Wed, 28 Jan 2009 08:45:12 +0300, Daniel Keep 
 <daniel.keep.lists gmail.com> wrote:
 
 Honestly, I can't see what you're trying to accomplish.  It looks like
 you want something that's not called int, but which works exactly like
 an int does, and can be passed as one.

 If you just want another name for "int", you can use an alias.

 From the compiler's POV, there's no difference between "BlockNum" and 
 "int".

 alias int BlockNum;
 BlockNum a = 42;
 someFuncThatTakesAnInt(a);
If you want to have a type that is an int, but which won't allow itself to directly interact with ints, use a typedef. Personally, I like this usage for simple numeric values which I don't want to accidentally mix with other types. Yes, it's a bit of a pain to do arithmetic, but that's the trade-off you make. From the compiler's POV, "BlockNum" and "int" are totally distinct, incompatible types that just happen to be the same under the hood.
 typedef int BlockNum;
 BlockNum a = cast(BlockNum) 42;
 someFuncThatTakesAnInt(cast(int) a);
The last is if you need something that's basically an int, but you want it to behave differently. In that case, a struct with operators is your best bet. Let's say you wanted to do something like a Meters struct to store lengths. I'd do something like this:
 struct Meters {
     private int value;
     int asInt() { return value; }
     int asInt(int v) { return value=v; }
     // ... operator overloads ...
 }

 Meters a; a.asInt = 42;
 someFuncThatTakesAnInt( a.asInt );
I can't really offer more than that, since I don't know what it is you're trying to accomplish. -- Daniel
Perhaps, he wants opImplicitCast?
Jan 28 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Charles Hixson wrote:
 Suppose that you have four types, equivalent to, say, float.
 Call one of them Horiz, one Vertic, one Radians, and one Radius.
 These are all floats, but when you specify, say,
 float dist (Horiz x, Vert y)
 {  return sqrt(x * x + y * y);  }
 It's important that the arguments aren't Radius and Radians.  Or Horiz
 and Horiz.
 
 [snip]
There's no problem with that: make them structs that implement the appropriate operators. See, what I don't get (note: this is how I perceive it) is the desire to have this sort of type protection, but require the compiler to somehow be psychic in order to know when and where you don't care and throw it away. You either have distinct types that aren't automatically compatible, or you don't. -- Daniel
Jan 28 2009
parent Charles Hixson <charleshixsn earthlink.net> writes:
Daniel Keep wrote:
 
 Charles Hixson wrote:
 Suppose that you have four types, equivalent to, say, float.
 Call one of them Horiz, one Vertic, one Radians, and one Radius.
 These are all floats, but when you specify, say,
 float dist (Horiz x, Vert y)
 {  return sqrt(x * x + y * y);  }
 It's important that the arguments aren't Radius and Radians.  Or Horiz
 and Horiz.

 [snip]
There's no problem with that: make them structs that implement the appropriate operators. See, what I don't get (note: this is how I perceive it) is the desire to have this sort of type protection, but require the compiler to somehow be psychic in order to know when and where you don't care and throw it away. You either have distinct types that aren't automatically compatible, or you don't. -- Daniel
No such requirement. If it's based on a type, then it can be used as that type. But the converse shouldn't be true. I.e., in the example it should require extra effort to use a float where a Horiz was requested. (Cast would reasonably be required here.) But to use a Horiz as a float should be trivial. Actually, typedef almost works for this. The problem is that if, e.g., one defines typedef uint MyType; and then does writef (MyType); the compiler asks whether I mean to use a byte or a ulong. It doesn't automatically realize that the appropriate type is uint. (Again, this is D2.023 on Linux.)
Jan 28 2009