Adding to a global AliasSeq

Basile B. b2.temp at gmx.com
Tue Aug 15 07:02:45 UTC 2023


On Tuesday, 15 August 2023 at 02:30:37 UTC, repr_man wrote:
> Consider the following template mixin:
> ```d
> mixin template Base()
> {
>     int x(){ return 10; }
> }
> ```
> It could be used in a variety of structs as follows:
> ```d
> struct Child
> {
>     mixin Base!();
> }
> ```
> Now, let's suppose we write a function with a parameter that 
> should only be able to take items that mix in `Base`:
> ```d
> auto f(T)(T arg)
> {
>     return arg.x;
> }
> ```
> This works because D uses structural subtyping.  However, this 
> creates a problem: if we make another struct that looks like it 
> mixes in `Base` and pass it to the function, we could get 
> unexpected results:
> ```d
> struct Other
> {
>     int x = 5;
> }
>
> unittest
> {
>     import std.stdio;
>
>     auto c = Child();
>     auto o = Other();
>
>     writeln(f(c));
>     writeln(f(o));
> }
> ```
> The output from running `dub test` is as follows:
> ```
> 10
> 5
> ```
> [...]
> Is there any template-fu of which I am not aware that would 
> make the thing I am trying to do possible?

You can add a kind of tag with the mixin, that will allow to to 
test if the type supplied to `f` is well a Base implementor:

```d
mixin template Base()
{
     enum LookImABase = true;
     int x(){ return 10; }
}

struct Child
{
     mixin Base!();
}

auto f(T)(T arg)
if (is(typeof(T.LookImABase)))
{
     return arg.x;
}

struct Other
{
     int x = 5;
}

void main(string[] args)
{
     import std.stdio;

     auto c = Child();
     auto o = Other();

     writeln(f(c)); // ok
     writeln(f(o)); // error
}
```

This is what is called in D the "design by introspection". There 
are alternatives way of doing that, notably with UDAs.


```d
mixin template Base()
{
     enum Base;
     @Base int x(){ return 10; }
}
```

But the introspection code is more complex (std.traits getUDA, 
hasUDA)

Finally a different approach, with no introspection, is to use a 
named mixin:

```d
mixin template Base()
{
     enum Base;
     int x(){ return 10; }
}

struct Child
{
     mixin Base!() base;
}

auto f(T)(T arg)
{
     return arg.base.x;
}

struct Other
{
     int x = 5;
}

void main(string[] args)
{
     import std.stdio;

     auto c = Child();
     auto o = Other();

     writeln(f(c));
     writeln(f(o)); // error
}
```

which works but has the same problem as what lead you to ask your 
question, for example if it turns out that the supplied argument 
has unfortunately a `base` member that itself has a `x` member.


More information about the Digitalmars-d-learn mailing list