www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Trait for "can be instantiated"?

reply Ben Jones <fake fake.fake> writes:
I have a struct template that takes an alias parameter and I'm 
trying to distinguish between type parameters and enum values.  
std.traits.isType works for this except for one edge case:

```
import std.traits;
import std.stdio;

struct S{}
enum x;
enum y = 5;

struct Wrap(alias T) {
     static if(isType!T){
          T value; //When t == x this doesn't work because `x` is 
opaque and has no default initializer
     }
}

void main()
{
     pragma(msg, isType!x); //true
     pragma(msg, isType!y); //false

     Wrap!S ws; //OK
     Wrap!x wx; //error instantiating
     Wrap!y wy; //OK, because we the static if condition is false
}
```

x is a "type" as far as isType is concerned (which makes sense to 
me), but I don't think you can ever declare a variable of that 
type since there's no way to initialize it... Is there a trait 
that can tell if you can initialize a variable of a certain type? 
  The best I can think of is __traits(compiles, "T x;"), but it 
seems like it should be possible to do better?
May 09 2022
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 5/9/22 14:24, Ben Jones wrote:

 Is there a trait that can tell if you
 can initialize a variable of a certain type?
Not answering that question but the 'is' expression seems to work in this case: static if(is (T)) { T value; } https://dlang.org/spec/expression.html#IsExpression Ali
May 09 2022
parent reply Ben Jones <fake fake.fake> writes:
On Monday, 9 May 2022 at 21:58:59 UTC, Ali Çehreli wrote:
 On 5/9/22 14:24, Ben Jones wrote:

 Is there a trait that can tell if you
 can initialize a variable of a certain type?
Not answering that question but the 'is' expression seems to work in this case: static if(is (T)) { T value; } https://dlang.org/spec/expression.html#IsExpression Ali
Using is(T) instead of isType!T also appears to be true for the un-instantiate-able enum. Was your point just that I could replace isType!T with is(T)?
May 09 2022
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 5/9/22 20:52, Ben Jones wrote:

 Using is(T) instead of isType!T also appears to be true for the
 un-instantiate-able enum.  Was your point just that I could replace
 isType!T with is(T)?
I just found something that looked like a workaround. I don't know whether it is by design or by accident or whether this exercise exposes a compiler or language bug. Sorry... :) Ali
May 09 2022
parent reply Salih Dincer <salihdb hotmail.com> writes:
On Tuesday, 10 May 2022 at 04:30:28 UTC, Ali Çehreli wrote:
 I just found something that looked like a workaround. I don't 
 know whether it is by design or by accident or whether this 
 exercise exposes a compiler or language bug. Sorry... :)
I can't exactly use the is(T == char) or is(T : char) check. It isn't happening! But I still have a little more patience :) SDB 79
May 10 2022
parent Salih Dincer <salihdb hotmail.com> writes:
On Tuesday, 10 May 2022 at 07:18:53 UTC, Salih Dincer wrote:
 But I still have a little more patience :)
It gives an error when the exclamation mark is removed, I have no objections. But typing long instead of char also gives an error! ```d void main() { alias T = char; import std.traits; foreach(useableType; AllImplicitConversionTargets!(ulong)) { /* Except for four: long, float, double, real. The approval.//*/ assert(!is(T == useableType)); } // No errors! } ``` **Reference:** https://dlang.org/phobos/std_traits.html#AllImplicitConversionTargets SDB 79
May 10 2022
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
On 09.05.22 23:24, Ben Jones wrote:
 enum x;
 enum y = 5;
 
 struct Wrap(alias T) {
      static if(isType!T){
           T value; //When t == x this doesn't work because `x` is
opaque 
 and has no default initializer
      }
 }
[...]
 
 x is a "type" as far as isType is concerned (which makes sense to me), 
 but I don't think you can ever declare a variable of that type since 
 there's no way to initialize it... Is there a trait that can tell if you 
 can initialize a variable of a certain type?  The best I can think of is 
 __traits(compiles, "T x;"), but it seems like it should be possible to 
 do better?
`x` is a type, period. You can use void initialization to declare values of types that don't have an `init` value: `x value = void;` As for an alternative to the brute force `__traits(compiles, ...)`, you can check if `T.init` is a thing: static if (is(typeof(T.init))) { T value; } I'm not sure if that's really better, though. By the way, what is your `Wrap` supposed to do with `x`? Treating it like `y` will likely fail, too, because `x` is not a value.
May 09 2022
parent reply Ben Jones <fake fake.fake> writes:
On Tuesday, 10 May 2022 at 05:45:25 UTC, ag0aep6g wrote:
 `x` is a type, period.

 You can use void initialization to declare values of types that 
 don't have an `init` value: `x value = void;`

 As for an alternative to the brute force `__traits(compiles, 
 ...)`, you can check if `T.init` is a thing:

     static if (is(typeof(T.init))) { T value; }

 I'm not sure if that's really better, though.

 By the way, what is your `Wrap` supposed to do with `x`? 
 Treating it like `y` will likely fail, too, because `x` is not 
 a value.
I'm writing a lexer and I'm using sumtype to store any of the token types. Some have values associated with them (like brackets and parens which are defined as `enum lparen = '('` or whatever) and some are just markers (keywords like 'if', which I'm trying to represent with just `enum if_token` ). The wrapper struct is there because I need a type for each one to use them as part of a sumtype and I only want to store the enum's value when it makes sense to.
May 10 2022
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, May 10, 2022 at 03:26:28PM +0000, Ben Jones via Digitalmars-d-learn
wrote:
[...]
 I'm writing a lexer and I'm using sumtype to store any of the token types.
 Some have values associated with them (like brackets and parens which are
 defined as `enum lparen = '('` or whatever) and some are just markers
 (keywords like 'if', which I'm trying to represent with just `enum if_token`
 ). The wrapper struct is there because I need a type for each one to use
 them as part of a sumtype and I only want to store the enum's value when it
 makes sense to.
A sum type in the case of token types is essentially the same thing as an enum value, implementation-wise. Sum types are implemented essentially as a discriminated union, which consists of a tag and a variant payload. The tag essentially behaves like an enum (and is in fact often implemented as such), which determines the interpretation of the payload. Using wrapper structs, etc., for this is IMO total overkill. Just use an enum for your token types. Something like this would suffice: enum TokenType { lparen, rparen, if_token, ... string_literal, } struct Token { TokenType type; union { string stringval; float floatval; ... // etc. } } T -- Two wrongs don't make a right; but three rights do make a left...
May 10 2022
parent reply Ben Jones <fake fake.fake> writes:
On Tuesday, 10 May 2022 at 16:05:15 UTC, H. S. Teoh wrote:
 Using wrapper structs, etc., for this is IMO total overkill. 
 Just use an enum for your token types.  Something like this 
 would suffice:
That's basically what sumtype is going to do for me, but (hopefully) more safely. Also, the token types are "user defined," my lexer just grabs everything annotated with Token and passes those types/wrapped enums to sumtype.
May 10 2022
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, May 10, 2022 at 04:10:26PM +0000, Ben Jones via Digitalmars-d-learn
wrote:
 On Tuesday, 10 May 2022 at 16:05:15 UTC, H. S. Teoh wrote:
 Using wrapper structs, etc., for this is IMO total overkill. Just
 use an enum for your token types.  Something like this would
 suffice:
That's basically what sumtype is going to do for me, but (hopefully) more safely. Also, the token types are "user defined," my lexer just grabs everything annotated with Token and passes those types/wrapped enums to sumtype.
Ah, I see what you're trying to do. T -- Why are you blatanly misspelling "blatant"? -- Branden Robinson
May 10 2022
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Tuesday, 10 May 2022 at 16:10:26 UTC, Ben Jones wrote:
 On Tuesday, 10 May 2022 at 16:05:15 UTC, H. S. Teoh wrote:
 Using wrapper structs, etc., for this is IMO total overkill. 
 Just use an enum for your token types.  Something like this 
 would suffice:
That's basically what sumtype is going to do for me, but (hopefully) more safely. Also, the token types are "user defined," my lexer just grabs everything annotated with Token and passes those types/wrapped enums to sumtype.
How about being more explicit in the UDA ? The idea would be to associate the enum value to a type or not: ```d import std.traits; import std.stdio; struct Token(T); struct Token(T...) if (T.length == 0) { } Token!(string) enum str_tok; Token!(float) enum float_tok; Token!() enum lparen_tok; void main() { alias toks = getSymbolsByUDA!(mixin(__MODULE__), Token); static foreach (t; toks) {{ alias U = getUDAs!(t, Token); alias A = TemplateArgsOf!(U); static if (A.length) pragma(msg, "add a `" ~ A[0].stringof ~ "`for `" ~ t.stringof ~ "`"); else pragma(msg, "no SumType data needed for `" ~ t.stringof ~ "`"); }} } ```
May 11 2022
parent Ben Jones <fake fake.fake> writes:
On Wednesday, 11 May 2022 at 12:29:05 UTC, Basile B. wrote:
 How about being more explicit in the UDA ?
 The idea would be to associate the enum value to a type or not:
I think that could work but would require some major changes to my existing code. Also, I think I'd prefer: ``` Token{ enum lparen = '('; enum rparen = ')'; enum if_token; } ``` to what you suggested as a user of the library.
May 11 2022