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.