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.

Reply via email to