On Saturday, 20 February 2021 at 09:16:46 UTC, Simon van Bernem wrote:
I have the following struct declaration:

struct Hash_Table(Key, Value, u32 delegate(ref Key) custom_hash_function = null)
[snip]
I take it from the error that the problem is not actually the delegate that I am passing, but the fact that the delegate type has another template parameter as the argument type. Can template arguments in D not reference previous template arguments? Is there some way I can get around this?

The D way would be an alias parameter:

struct Hash_Table(Key, Value, alias custom_hash_function) {
    // ...
}

Probably also adding a constraint:

struct Hash_Table(Key, Value, alias custom_hash_function)
if (is_valid_hash_function!(Key, custom_hash_function))
{
    // ...
}

// Check if the hash function can be called with a Key
// as argument, and the result be assigned to a u32
enum is_valid_hash_function(Key, alias fn) = __traits(compiles, (Key k){ u32 u = fn(k); });


D lets you refer to other template parameters in the same template parameter list in three cases, as far as I know:

1) A type may be a subtype of an earlier parameter:

    class C {}
    struct S(TBase, TDerived : TBase) {}
    S!(Object, C) a;


2) A type parameter with a type specialization followed by template parameter list:

    struct Fn(U) {}
    struct S(T: Fn!Arg, Arg) {}
    S!(Fn!int) a;


3) Following a type parameter, a value parameter of that type may appear:

    struct S(T, T value) {}
    S!(int, 4) a;


The first two can also be combined:

    struct Fn(U) {}
    struct S(T1, T2: Fn!T3, T3 : T1) {}
    S!(int, Fn!int) a;


However, it seems 3) only works with that specific type, modulo storage classes. No Foo!T, no void delegate(T). At any rate, the alias parameter is the way it's generally done, and is more flexible, so just leave it at that.

--
  Simen

Reply via email to