Making one struct work in place of another for function calls.
cc
cc at nevernet.com
Wed Apr 17 15:45:28 UTC 2024
On Wednesday, 17 April 2024 at 03:13:46 UTC, Liam McGillivray
wrote:
> On Wednesday, 17 April 2024 at 02:39:25 UTC, Paul Backus wrote:
>> This is called [row polymorphism][1], and it does not exist in
>> D.
>>
>> You could approximate it by making `someFunction` a template,
>> and accepting any type `T` that has the necessary members
>> instead of only accepting `typeB`. But this is only possible
>> if you are free to modify the definition of `someFunction`.
>
> Is there a way I can replace "`TypeB`" in the function
> parameters with another symbol, and then define that symbol to
> accept `TypeB` as an argument, but also accept `TypeA` which
> would get converted to `TypeB` using a function? I'm willing to
> make a function template if it's rather simple.
Normal template approach would be as simple as:
```d
struct TypeA {
int x, y;
string name;
}
struct TypeB {
float x, y;
ubyte[] data;
}
// Strict version, only allows the named structs
float someFunction(S)(S input) if (is(S == TypeA) || is(S ==
TypeB)) {
writefln("input loc: %s,%s", input.x, input.y);
return 0;
}
void main() {
someFunction(TypeA(1,1));
someFunction(TypeB(2,2));
}
```
Or you could write the function like:
```d
// Permission version, accepts any struct with the required
members
float someFunction(S)(S input) if (is(S == struct) &&
isNumeric!(typeof(S.x)) && isNumeric!(typeof(S.y))) {
writefln("input loc: %s,%s", input.x, input.y);
return 0;
}
```
In fact, you don't even necessarily need the template constraints:
```d
// It just works... usually
float someFunction(S)(S input) {
writefln("input loc: %s,%s", input.x, input.y);
return 0;
}
```
But then you might get (at best) less clear error messages when a
wrong type is passed, and (at worst) funny business if someone
passes a type that technically satisfies the function behavior
but isn't actually a type you expected and is treating those
members differently. It's often ideal to have either some type
of template constraints, or static ifs/static asserts in the
function body so you know what you're dealing with.
Another solution, without templates, if you can for instance
modify TypeB but not TypeA, is to give TypeB a constructor that
takes a TypeA as an argument.
```d
struct TypeA {
int x, y;
string name;
}
struct TypeB {
float x, y;
immutable(ubyte)[] data;
this(float x, float y) { // We need a constructor here now too
this.x = x;
this.y = y;
}
this(TypeA a) {
this.x = a.x;
this.y = a.y;
this.data = cast(immutable(ubyte)[]) a.name;
}
}
float someFunction(TypeB input) {
writefln("input loc: %s,%s", input.x, input.y);
return 0;
}
auto someFunction(TypeA input) => someFunction(TypeB(input));
```
If you cannot modify either struct definition, you could do the
conversion by hand in the stub instead.
Additionally, if you have many functions and you don't want to
write stubs for all of them, you could use mixins to generate
them for you like so:
```d
float someFunctionUno(TypeB input) {
writefln("uno loc: %s,%s", input.x, input.y);
return 0;
}
float someFunctionDos(TypeB input) {
writefln("dos loc: %s,%s", input.x, input.y);
return 0;
}
float someFunctionTres(TypeB input) {
writefln("tres loc: %s,%s", input.x, input.y);
return 0;
}
// Consider also UDAs, iterating member functions, etc
static foreach (sym; "someFunctionUno someFunctionDos
someFunctionTres".split) {
mixin(format(`auto %s(TypeA input) => %s(TypeB(input));`, sym,
sym));
}
void main() {
someFunctionUno(TypeA(1,1));
someFunctionDos(TypeB(2,2));
}
```
There are yet other ways to do it as well. The solution you'll
use will depend on more detailed specifics. In many cases, when
working with similar data types and you want the function itself
to be as agnostic as possible about what it's dealing with,
templates are often the way to go. D's
templating/metaprogramming capacities are extremely powerful and
flexible. However, it may not necessarily be the right choice if
you need to rely on very specific handling of the data types in
question and their layouts.
More information about the Digitalmars-d-learn
mailing list