www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Template Constraints

reply Jonathan <JonathanILevi gmail.com> writes:
I am having trouble finding many useful explanations of using 
template constraints beyond basic usage.

I would like to have a template constrant to enforce that a type 
can be explicitly cast to another type:

     void (T)(T t)
             if (cast(int) T)//force `cast(int) T` to be possible
         {
             // Yay I know `t` can be cast to an `int`!
     }

Is this possible?
Feb 23 2018
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 24 February 2018 at 02:54:13 UTC, Jonathan wrote:
 I am having trouble finding many useful explanations of using 
 template constraints beyond basic usage.
The constraint is just like static if as to what it allows inside, so you can check almost anything in there. Like for the cast, you might do void name(T)(T t) if(__traits(compiles, cast(int) t) {} just seeing it the cast compiles. You might also do if(is(T : int)) which asks if T is implicitly convertible to int. But since you want explicit cast, the compiles is prolly the way to go. is: https://dlang.org/spec/expression.html#IsExpression compiles: https://dlang.org/spec/traits.html#compiles
Feb 23 2018
parent Jonathan <JonathanILevi gmail.com> writes:
On Saturday, 24 February 2018 at 03:04:07 UTC, Adam D. Ruppe 
wrote:
 On Saturday, 24 February 2018 at 02:54:13 UTC, Jonathan wrote:
 I am having trouble finding many useful explanations of using 
 template constraints beyond basic usage.
The constraint is just like static if as to what it allows inside, so you can check almost anything in there. Like for the cast, you might do void name(T)(T t) if(__traits(compiles, cast(int) t) {} just seeing it the cast compiles. You might also do if(is(T : int)) which asks if T is implicitly convertible to int. But since you want explicit cast, the compiles is prolly the way to go. is: https://dlang.org/spec/expression.html#IsExpression compiles: https://dlang.org/spec/traits.html#compiles
Thanks, this was just what I needed to know.
Feb 24 2018
prev sibling next sibling parent reply psychoticRabbit <meagain meagain.com> writes:
On Saturday, 24 February 2018 at 02:54:13 UTC, Jonathan wrote:
 I am having trouble finding many useful explanations of using 
 template constraints beyond basic usage.

 I would like to have a template constrant to enforce that a 
 type can be explicitly cast to another type:

     void (T)(T t)
             if (cast(int) T)//force `cast(int) T` to be possible
         {
             // Yay I know `t` can be cast to an `int`!
     }

 Is this possible?
I would have thought contracts would be ideal here? https://dlang.org/spec/contracts.html
Feb 23 2018
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, February 24, 2018 03:04:53 psychoticRabbit via Digitalmars-d-
learn wrote:
 On Saturday, 24 February 2018 at 02:54:13 UTC, Jonathan wrote:
 I am having trouble finding many useful explanations of using
 template constraints beyond basic usage.

 I would like to have a template constrant to enforce that a

 type can be explicitly cast to another type:
     void (T)(T t)

             if (cast(int) T)//force `cast(int) T` to be possible

         {

             // Yay I know `t` can be cast to an `int`!

     }

 Is this possible?
I would have thought contracts would be ideal here? https://dlang.org/spec/contracts.html
Contracts are used to assert runtime state, whereas template constraints control which arguments can be used with the template (including being used for function overloading). The OP wants his function template to reject any arguments that can't be explicitly cast to int, whereas an in contract would be used for something like verifying at runtime that the argument was within a particular range of values. - Jonathan M Davis
Feb 23 2018
prev sibling next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, February 24, 2018 02:54:13 Jonathan via Digitalmars-d-learn 
wrote:
 I am having trouble finding many useful explanations of using
 template constraints beyond basic usage.

 I would like to have a template constrant to enforce that a type
 can be explicitly cast to another type:

      void (T)(T t)
              if (cast(int) T)//force `cast(int) T` to be possible
          {
              // Yay I know `t` can be cast to an `int`!
      }

 Is this possible?
Well, template constraints in general usually either test that the type of one expression matches another or that a particular piece of code compiles. So, you'd need to test that the cast compiles. That requires either an is expression or __traits(compiles, ...) (or an eponymous template that contains such an expression). In this case, you could probably just do if(is(typeof(cast(int)t))) since as long as the result of the expression isn't void, the is expression will be true. And note that the expression uses t, not T. You can't cast the type itself. You have to cast a value of that type. - Jonathan M Davis
Feb 23 2018
prev sibling next sibling parent reply psychoticRabbit <meagain meagain.com> writes:
On Saturday, 24 February 2018 at 02:54:13 UTC, Jonathan wrote:
 I am having trouble finding many useful explanations of using 
 template constraints beyond basic usage.

 I would like to have a template constrant to enforce that a 
 type can be explicitly cast to another type:

     void (T)(T t)
             if (cast(int) T)//force `cast(int) T` to be possible
         {
             // Yay I know `t` can be cast to an `int`!
     }

 Is this possible?
import std.traits : isIntegral; void testTemplate(T)(T x) if (isIntegral!T) { writeln(x, " is an integral. yeah!"); }
Feb 23 2018
next sibling parent psychoticRabbit <meagain meagain.com> writes:
On Saturday, 24 February 2018 at 03:30:45 UTC, psychoticRabbit 
wrote:
 On Saturday, 24 February 2018 at 02:54:13 UTC, Jonathan wrote:
 I am having trouble finding many useful explanations of using 
 template constraints beyond basic usage.

 I would like to have a template constrant to enforce that a 
 type can be explicitly cast to another type:

     void (T)(T t)
             if (cast(int) T)//force `cast(int) T` to be 
 possible
         {
             // Yay I know `t` can be cast to an `int`!
     }

 Is this possible?
import std.traits : isIntegral; void testTemplate(T)(T x) if (isIntegral!T) { writeln(x, " is an integral. yeah!"); }
or this is probably more suitable ;-) (should you really be using an explicity convert anyway?) void testTemplate2(T)(T x) if (isImplicitlyConvertible!(T, int)) { writeln(x, " is implicitly convertible to an int. yeah!"); }
Feb 23 2018
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, February 24, 2018 03:30:45 psychoticRabbit via Digitalmars-d-
learn wrote:
 On Saturday, 24 February 2018 at 02:54:13 UTC, Jonathan wrote:
 I am having trouble finding many useful explanations of using
 template constraints beyond basic usage.

 I would like to have a template constrant to enforce that a

 type can be explicitly cast to another type:
     void (T)(T t)

             if (cast(int) T)//force `cast(int) T` to be possible

         {

             // Yay I know `t` can be cast to an `int`!

     }

 Is this possible?
import std.traits : isIntegral; void testTemplate(T)(T x) if (isIntegral!T) { writeln(x, " is an integral. yeah!"); }
That does not do what the OP requested at all. That tests whether T is one of byte, ubyte, short, ushort, int, uint, long, and ulong, whereas what the OP wants is to test whether T can be cast to int. - Jonathan M Davis
Feb 23 2018
parent reply psychoticRabbit <meagain meagain.com> writes:
On Saturday, 24 February 2018 at 03:43:25 UTC, Jonathan M Davis 
wrote:
 That does not do what the OP requested at all. That tests 
 whether T is one of byte, ubyte, short, ushort, int, uint, 
 long, and ulong, whereas what the OP wants is to test whether T 
 can be cast to int.

 - Jonathan M Davis
yeah. I realised that after I had posted. I posted a more suitable response after that though (I hope), with the intention of leading the OP away from an explicit cast, towards an implicit cast.
Feb 23 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, February 24, 2018 03:48:44 psychoticRabbit via Digitalmars-d-
learn wrote:
 On Saturday, 24 February 2018 at 03:43:25 UTC, Jonathan M Davis

 wrote:
 That does not do what the OP requested at all. That tests
 whether T is one of byte, ubyte, short, ushort, int, uint,
 long, and ulong, whereas what the OP wants is to test whether T
 can be cast to int.

 - Jonathan M Davis
yeah. I realised that after I had posted. I posted a more suitable response after that though (I hope), with the intention of leading the OP away from an explicit cast, towards an implicit cast.
Whether an implicit cast or an explicit cast makes more sense depends entirely on what the code is doing, but either way, the conversion needs to be forced inside the function, or you end up with bugs. Far too often, when someone has a template constraint that checks an implicit conversion, the function doesn't actually force the conversion, and that can do anything from resulting in some instantiations not compiling to causing subtle bugs due to the argument being used without being converted. In general, it's actually best to avoid conversions entirely with generic code and force the caller to do the conversion if a conversion is appropriate. But ultimately, what works best depends on what the code is trying to do. - Jonathan M Davis
Feb 23 2018
parent reply psychoticRabbit <meagain meagain.com> writes:
On Saturday, 24 February 2018 at 03:58:48 UTC, Jonathan M Davis 
wrote:
 Whether an implicit cast or an explicit cast makes more sense 
 depends entirely on what the code is doing, but either way, the 
 conversion needs to be forced inside the function, or you end 
 up with bugs. Far too often, when someone has a template 
 constraint that checks an implicit conversion, the function 
 doesn't actually force the conversion, and that can do anything 
 from resulting in some instantiations not compiling to causing 
 subtle bugs due to the argument being used without being 
 converted. In general, it's actually best to avoid conversions 
 entirely with generic code and force the caller to do the 
 conversion if a conversion is appropriate.

 But ultimately, what works best depends on what the code is 
 trying to do.

 - Jonathan M Davis
yeah it's hard to say much more without knowing what the code really wants to do..but presumably, you'd want to incorporate some contract programming in such a solution too, particulary given there's something potentially dodgy going on within such a function.
Feb 23 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, February 24, 2018 04:13:30 psychoticRabbit via Digitalmars-d-
learn wrote:
 On Saturday, 24 February 2018 at 03:58:48 UTC, Jonathan M Davis

 wrote:
 Whether an implicit cast or an explicit cast makes more sense
 depends entirely on what the code is doing, but either way, the
 conversion needs to be forced inside the function, or you end
 up with bugs. Far too often, when someone has a template
 constraint that checks an implicit conversion, the function
 doesn't actually force the conversion, and that can do anything
 from resulting in some instantiations not compiling to causing
 subtle bugs due to the argument being used without being
 converted. In general, it's actually best to avoid conversions
 entirely with generic code and force the caller to do the
 conversion if a conversion is appropriate.

 But ultimately, what works best depends on what the code is
 trying to do.

 - Jonathan M Davis
yeah it's hard to say much more without knowing what the code really wants to do..but presumably, you'd want to incorporate some contract programming in such a solution too, particulary given there's something potentially dodgy going on within such a function.
Why is there anything dodgy going on and why would you need contracts? Contracts actually tend to go very badly with generic code, because whatever they assert has to be generic, and while that works sometimes, more often than not, it doesn't. If you're testing for a conversion in a template constraint, simply forcing the conversion by assigning it to a variable of the target type (with an explicit cast if necessary) solves all of the problems related to testing for a conversion and then writing the code as if the argument were of the target type rather than a type that converted to the target type. - Jonathan M Davis
Feb 23 2018
parent reply psychoticRabbit <meagain meagain.com> writes:
On Saturday, 24 February 2018 at 04:22:12 UTC, Jonathan M Davis 
wrote:
 Why is there anything dodgy going on and why would you need 
 contracts? Contracts actually tend to go very badly with 
 generic code, because whatever they assert has to be generic, 
 and while that works sometimes, more often than not, it doesn't.

 - Jonathan M Davis
what if 3.3 is passed to the template, and it explicately casts it to an int. To me, that would be dodgy - unless there was a contract, that I had accepted and agreed to, so that this not dodgy.
Feb 23 2018
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, February 24, 2018 04:33:52 psychoticRabbit via Digitalmars-d-
learn wrote:
 On Saturday, 24 February 2018 at 04:22:12 UTC, Jonathan M Davis

 wrote:
 Why is there anything dodgy going on and why would you need
 contracts? Contracts actually tend to go very badly with
 generic code, because whatever they assert has to be generic,
 and while that works sometimes, more often than not, it doesn't.

 - Jonathan M Davis
what if 3.3 is passed to the template, and it explicately casts it to an int. To me, that would be dodgy
It could be exactly how the function is intended to work, since that's how casting to int works for float. And there's nothing dodgy about a cast from float to int losing the part of the value to the right of the decimal place. That's the expected behavior.
 - unless there was a contract, that I
 had accepted and agreed to, so that this not dodgy.
All contracts are are assertions. That's it. There's nothing special about them. An in contract is used to verify that the function is given valid data, but there really isn't any accepting or agreeing to a contract. Rather, it's something that blows up in your face if you give it bad data so that you can catch bugs. Presumably, the documentation gives the requirements for the function if it has them, and then an in contract can be used to verify that the arguments don't violate those requirements, but all it is is a tool for catching bugs. And there isn't necesarily anything buggy about casting 3.3 to an int. That depends entirely on what the code is supposed to be doing. Now, by having the function simply accept int you avoid the entire question, because then it's up to the caller to decide how they go about converting to int, and I'd argue that that's better in general, but there are times when casting within the function may make more sense. - Jonathan M Davis
Feb 23 2018
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Feb 24, 2018 at 02:54:13AM +0000, Jonathan via Digitalmars-d-learn
wrote:
 I am having trouble finding many useful explanations of using template
 constraints beyond basic usage.
 
 I would like to have a template constrant to enforce that a type can
 be explicitly cast to another type:
 
     void (T)(T t)
             if (cast(int) T)//force `cast(int) T` to be possible
         {
             // Yay I know `t` can be cast to an `int`!
     }
 
 Is this possible?
Yes: void (T)(T t) if (is(typeof(cast(int) T.init))) { ... } Explanation: - is(X) generally means "is X a valid type?". It's the usual way of testing whether something is valid, because an invalid expression will have no type, and is(X) will return false for it. - To make use of is(X), generally you want to use typeof to extract the type of some test expression. - T.init is the usual D way of saying "give me an instance of type T", because every type has an .init. - Putting it together, we have our test object T.init, and our test expression `cast(int) T.init`, extract the type of that using typeof, and use the is(...) operator to test whether that type exists. T -- I think Debian's doing something wrong, `apt-get install pesticide', doesn't seem to remove the bugs on my system! -- Mike Dresser
Feb 23 2018