## digitalmars.D.learn - Chain two different struct specialization

• Andrea Fontana (16/16) Mar 01 2013 I'm trying to do something like this. I don't know whether or not
• Andrea Fontana (32/48) Mar 01 2013 Ops. Here the complete message:
• bearophile (5/6) Mar 01 2013 ==>
• Andrea Fontana (40/46) Mar 01 2013 Sure not the only error. I was writing "pseudo code". Real code
• bearophile (32/34) Mar 01 2013 One solution is to not use templates:
• Era Scarecrow (28/48) Mar 01 2013 With templates there's a few different ways you can consider
• Andrea Fontana (11/62) Mar 01 2013 Maybe if i have MyStruct!Weights1 arr[]; MyStruct!Weights2
"Andrea Fontana" <nospam example.com> writes:
```I'm trying to do something like this. I don't know whether or not
it's a good idea, i'm open to solutions and suggestions

struct MyStruct(WEIGHTS)
{
string ...
string ...

alias WEIGHTS weights;
}

double likeness(T,T1)(ref in T1, ref in T2)
{
// Here i do some complex calculus using struct fields
// and using weights consts
}

enum FirstWeights : double
{

}
```
Mar 01 2013
"Andrea Fontana" <nospam example.com> writes:
```On Friday, 1 March 2013 at 14:27:40 UTC, Andrea Fontana wrote:
I'm trying to do something like this. I don't know whether or
not it's a good idea, i'm open to solutions and suggestions

struct MyStruct(WEIGHTS)
{
string ...
string ...

alias WEIGHTS weights;
}

double likeness(T,T1)(ref in T1, ref in T2)
{
// Here i do some complex calculus using struct fields
// and using weights consts
}

enum FirstWeights : double
{

}

Ops. Here the complete message:

I'm trying to do something like this. I don't know whether or not
it's a good idea, i'm open to solutions and suggestions

struct MyStruct(WEIGHTS)
{
string ...
string ...

alias WEIGHTS weights;
}

double likeness(T,T1)(ref in T1, ref in T2)
{
// Here i do some complex calculus using struct fields
// and using weights consts
}

enum FirstWeights : double
{
double foo = 0.3,
double bar = 0.4
}

enum SecondWeights : double
{
double foo = 0.3,
double bar = 0.4
}

so:
auto s1 = MyStruct!FirstWeights ...
auto s2 = MyStruct!SecondWeights ...

likeness(s1,s2); // works

But I can't mix different type (s1,s2) on a single range or
array, to check for likeness, is there a way? (or should I avoid
template init a double[] weights field)
```
Mar 01 2013
"bearophile" <bearophileHUGS lycos.com> writes:
```Andrea Fontana:

double likeness(T,T1)(ref in T1, ref in T2)

==>

double likeness(T1, T2)(in ref T1, in ref T2)

Bye,
bearophile
```
Mar 01 2013
"Andrea Fontana" <nospam example.com> writes:
```On Friday, 1 March 2013 at 14:39:53 UTC, bearophile wrote:
Andrea Fontana:

double likeness(T,T1)(ref in T1, ref in T2)

==>

double likeness(T1, T2)(in ref T1, in ref T2)

Bye,
bearophile

Sure not the only error. I was writing "pseudo code". Real code
it's quite complex.

Try this one (is a really reduced working example):

struct MyStruct(WEIGHTS)
{
this (int p, int p2) { prop = p; prop2 = p2; }
int prop;
int prop2;

alias WEIGHTS weights;
}

double likeness(T1,T2)(ref in T1 first, ref in T2 second)
{
double v = (first.prop - second.prop) * first.weights.foo *
second.weights.foo;
v += (first.prop2 - second.prop2) * first.weights.bar *
second.weights.bar;
return v;
}

enum FirstWeights : double
{
foo = 0.3,
bar = 0.4
}

enum SecondWeights : double
{
foo = 0.5,
bar = 0.2
}

void main(string[] args)
{

auto s1 = MyStruct!FirstWeights(10,8);
auto s2 = MyStruct!FirstWeights(9, 10);
auto s3 = MyStruct!SecondWeights(9,10);

writeln(likeness(s1,s2)); // works
writeln(likeness(s1,s3)); // works

}

How to put s1,s2,3... in a range/array or something
similar/iterable? Probably there's no way (inside variant?)...
```
Mar 01 2013
"bearophile" <bearophileHUGS lycos.com> writes:
```Andrea Fontana:

How to put s1,s2,3... in a range/array or something
similar/iterable? Probably there's no way (inside variant?)...

One solution is to not use templates:

immutable struct Weights {
double foo, bar;
}

enum Weights firstWeights  = { foo: 0.3, bar: 0.4 },
secondWeights = { foo: 0.5, bar: 0.2 };

struct MyStruct {
int prop, prop2;
immutable Weights weights;

this ( Weights weights_, in int p, in int p2) pure nothrow {
prop = p;
prop2 = p2;
}
}

double likeness(in ref MyStruct first, in ref MyStruct second) {
double v = (first.prop - second.prop) * first.weights.foo *
second.weights.foo;
return v + (first.prop2 - second.prop2) * first.weights.bar *
second.weights.bar;
}

void main() {
immutable s1 = MyStruct(firstWeights,  10,  8);
immutable s2 = MyStruct(firstWeights,   9, 10);
immutable s3 = MyStruct(secondWeights,  9, 10);

import std.stdio;
writeln(likeness(s1, s2));
writeln(likeness(s1, s3));

const r = [s1, s2, s3];
}

Bye,
bearophile
```
Mar 01 2013
"bearophile" <bearophileHUGS lycos.com> writes:
``` void main() {
immutable s1 = MyStruct(firstWeights,  10,  8);
immutable s2 = MyStruct(firstWeights,   9, 10);
immutable s3 = MyStruct(secondWeights,  9, 10);

import std.stdio;
writeln(likeness(s1, s2));

Sorry for the mix of tabs and spaces. The crappy editor I have
used now has not replaced your tabs with spaces...

Bye,
bearophile
```
Mar 01 2013
"Andrea Fontana" <nospam example.com> writes:
```On Friday, 1 March 2013 at 15:08:21 UTC, bearophile wrote:
void main() {
immutable s1 = MyStruct(firstWeights,  10,  8);
immutable s2 = MyStruct(firstWeights,   9, 10);
immutable s3 = MyStruct(secondWeights,  9, 10);

import std.stdio;
writeln(likeness(s1, s2));

Sorry for the mix of tabs and spaces. The crappy editor I have
used now has not replaced your tabs with spaces...

Bye,
bearophile

That's right.

I think you missed an initialization on this(): I fixed it.

BTW, compiler can't guess s1 and s2 weights, should it?

if inside likeness() i write:

enum test = first.weights.foo * second.weights.foo;

it said that can't read first and second value at compile time.

Andrea
```
Mar 01 2013
"bearophile" <bearophileHUGS lycos.com> writes:
```Andrea Fontana:

BTW, compiler can't guess s1 and s2 weights, should it?

if inside likeness() i write:

enum test = first.weights.foo * second.weights.foo;

it said that can't read first and second value at compile time.

firstWeights and secondWeights are compile-time constants, but
the arguments you give to likeness are run-time values, so they
are unknown at compile-time inside likeness.

There are ways to solve that problem, but I don't know how much
good this is:

immutable struct Weights {
double foo, bar;
}

enum Weights firstWeights  = { foo: 0.3, bar: 0.4 },
secondWeights = { foo: 0.5, bar: 0.2 };

struct MyStruct {
int prop, prop2;
immutable Weights weights;

this(Weights weights_, in int p, in int p2) pure nothrow {
this.weights = weights_;
this.prop = p;
this.prop2 = p2;
}
}

double likeness(alias first, alias second)() {
enum test = first.weights.foo * second.weights.foo;
double v = (first.prop - second.prop) *
first.weights.foo * second.weights.foo;
return v + (first.prop2 - second.prop2) *
first.weights.bar * second.weights.bar;
}

void main() {
enum s1 = MyStruct(firstWeights,  10,  8);
enum s2 = MyStruct(firstWeights,   9, 10);
enum s3 = MyStruct(secondWeights,  9, 10);

import std.stdio;
writeln(likeness!(s1, s2)());
writeln(likeness!(s1, s3)());

const r = [s1, s2, s3];
}

Bye,
bearophile
```
Mar 01 2013
"Andrea Fontana" <nospam example.com> writes:
```On Friday, 1 March 2013 at 16:03:58 UTC, bearophile wrote:
Andrea Fontana:

BTW, compiler can't guess s1 and s2 weights, should it?

if inside likeness() i write:

enum test = first.weights.foo * second.weights.foo;

it said that can't read first and second value at compile time.

firstWeights and secondWeights are compile-time constants, but
the arguments you give to likeness are run-time values, so they
are unknown at compile-time inside likeness.

There are ways to solve that problem, but I don't know how much
good this is:

immutable struct Weights {
double foo, bar;
}

enum Weights firstWeights  = { foo: 0.3, bar: 0.4 },
secondWeights = { foo: 0.5, bar: 0.2 };

struct MyStruct {
int prop, prop2;
immutable Weights weights;

this(Weights weights_, in int p, in int p2) pure nothrow {
this.weights = weights_;
this.prop = p;
this.prop2 = p2;
}
}

double likeness(alias first, alias second)() {
enum test = first.weights.foo * second.weights.foo;
double v = (first.prop - second.prop) *
first.weights.foo * second.weights.foo;
return v + (first.prop2 - second.prop2) *
first.weights.bar * second.weights.bar;
}

void main() {
enum s1 = MyStruct(firstWeights,  10,  8);
enum s2 = MyStruct(firstWeights,   9, 10);
enum s3 = MyStruct(secondWeights,  9, 10);

import std.stdio;
writeln(likeness!(s1, s2)());
writeln(likeness!(s1, s3)());

const r = [s1, s2, s3];
}

Bye,
bearophile

but:

enum s1 = MyStruct(firstWeights,  10,  8);
enum s2 = MyStruct(firstWeights,   9, 10);

writeln(likeness(s1, s2));

still gives error: and s1 and s2 are known at compile time,
aren't them?

Your solution will create code for each item couple, isn't it?
Not good in my case :) Other ideas?
```
Mar 01 2013
"bearophile" <bearophileHUGS lycos.com> writes:
```Andrea Fontana:

but:

enum s1 = MyStruct(firstWeights,  10,  8);
enum s2 = MyStruct(firstWeights,   9, 10);

writeln(likeness(s1, s2));

still gives error: and s1 and s2 are known at compile time,
aren't them?

Right. But they are known at compile-time only outside likeness().

Your solution will create code for each item couple, isn't it?

Right.

Not good in my case :) Other ideas?

Another solution is to use indirection, with classes or unsafe
casts. Or to use variants. No solution is perfect.
Why do you want to perform those computations at compile-time?

Bye,
bearophile
```
Mar 01 2013
"Era Scarecrow" <rtcvb32 yahoo.com> writes:
```On Friday, 1 March 2013 at 14:32:12 UTC, Andrea Fontana wrote:
struct MyStruct(WEIGHTS)
{
string ...
string ...

alias WEIGHTS weights;
}

enum FirstWeights : double
{

}

enum SecondWeights : double
{
double foo = 0.3,
double bar = 0.4
}

so:
auto s1 = MyStruct!FirstWeights ...
auto s2 = MyStruct!SecondWeights ...

With templates there's a few different ways you can consider
handling it; I'm not recommending any of them but. Keeping it
simple, clean & easy to read is probably the best solution;
Although most of these won't help with any algorithms that need
them to be identical (unless cast or they are not templates).

1) If structs are the same size: You can forcibly cast it so one
will work with the other, however enums (or anything related
that's static or not stored with the struct) doesn't transfer
over and is lost.

I've tried this in my experimental polymorphic struct; Where the
methods differed slightly but data the structure is guaranteed to
be identical.

2) Conversion/Accessing differences: You can include a flag
specifying it's of a certain type or qualification, this can let
them interact if they are basically the same thing with something
minor under the hood different but that doesn't matter (or will
get in the way); Although unqual (or related in std.traits) might
be a better option for it, not sure myself.

struct MyStruct(WEIGHTS) {
enum isMyStruct = true;
}

void test(T1, T2)(T1 lhs, T2 rhs)
if (hasMember!(T1, "isMyStruct") && hasMember!(T2,
"isMyStruct")) {}

3) non-Template: As mentioned you can avoid templates &
different enums and instead use variables as appropriate.

I'm sure there's other ideas but I'm drawing a blank right now...
```
Mar 01 2013
"Andrea Fontana" <nospam example.com> writes:
```On Friday, 1 March 2013 at 16:38:39 UTC, Era Scarecrow wrote:
On Friday, 1 March 2013 at 14:32:12 UTC, Andrea Fontana wrote:
struct MyStruct(WEIGHTS)
{
string ...
string ...

alias WEIGHTS weights;
}

enum FirstWeights : double
{

}

enum SecondWeights : double
{
double foo = 0.3,
double bar = 0.4
}

so:
auto s1 = MyStruct!FirstWeights ...
auto s2 = MyStruct!SecondWeights ...

With templates there's a few different ways you can consider
handling it; I'm not recommending any of them but. Keeping it
simple, clean & easy to read is probably the best solution;
Although most of these won't help with any algorithms that need
them to be identical (unless cast or they are not templates).

1) If structs are the same size: You can forcibly cast it so
one will work with the other, however enums (or anything
related that's static or not stored with the struct) doesn't
transfer over and is lost.

I've tried this in my experimental polymorphic struct; Where
the methods differed slightly but data the structure is
guaranteed to be identical.

2) Conversion/Accessing differences: You can include a flag
specifying it's of a certain type or qualification, this can
let them interact if they are basically the same thing with
something minor under the hood different but that doesn't
matter (or will get in the way); Although unqual (or related in
std.traits) might be a better option for it, not sure myself.

struct MyStruct(WEIGHTS) {
enum isMyStruct = true;
}

void test(T1, T2)(T1 lhs, T2 rhs)
if (hasMember!(T1, "isMyStruct") && hasMember!(T2,
"isMyStruct")) {}

3) non-Template: As mentioned you can avoid templates &
different enums and instead use variables as appropriate.

I'm sure there's other ideas but I'm drawing a blank right
now...

Maybe if i have MyStruct!Weights1 arr[];  MyStruct!Weights2
arr2[];
instead of trying to merge them, and call:
myfunc(singlebigarray);

I should write a template function with a variadic number of
params:
myfunc(arr1, arr2, arr3 etc...);

Or simply come back to basic ineritance or previous solution, was
just a challenge to find a compile time way to pre-compute all
consts factors...
```
Mar 01 2013