Thread overview
staticMap but with two arguments
Feb 06, 2023
John Chapman
Feb 06, 2023
Ali Çehreli
Feb 06, 2023
John Chapman
Feb 08, 2023
John Chapman
Feb 09, 2023
Ali Çehreli
Feb 09, 2023
John Chapman
Feb 09, 2023
Ali Çehreli
February 06, 2023

I have two AliasSeqs: one containing a function's parameters (SourceSeq), the other containing the types I want to convert said parameters to (TargetSeq). I'd use something like staticMap to call the conversion function with both a parameter from SourceSeq and a type from TargetSeq, and return an AliasSeq of converted values which will be forwarded to another function. staticMap's "fun" can only be instantiated with a single argument, while I need it to work with two.

E.g.:

template toTarget(alias source, Target) {
  static if (is(typeof(source) == int) && is(Target == string)) // for example, convert int to string
}

alias TargetSeq = Parameters!targetFunc;

auto wrapperFunc(A...)(A) {
  alias SourceSeq = __traits(parameters);
  return targetFunc(staticMap!(toTarget, SourceSeq)); // How would I call staticMap (or something similar) with SourceSeq *and* TargetSeq?
}

I could build the list of converted values manually but I wanted something smart (like staticMap) to do it inline. I thought ApplyLeft/Right could help but couldn't get my head around it.

February 06, 2023
On 2/5/23 17:20, John Chapman wrote:

> staticMap's "fun" can only be
> instantiated with a single argument, while I need it to work with two.

I adapted staticMap's implementation to two sets of arguments:

import std.meta : AliasSeq;

// The first half of 'args' is the "first arguments" and
// the second half is the "second arguments".
//
// (This can be generalized to N sets of arguments.)
template staticMap2(alias fun, args...)
{
    alias firsts = args[0 .. $ / 2];
    alias seconds = args[$ / 2 .. $];

    static assert(firsts.length == seconds.length,
                  "Mismatched number of first and second arguments");

    alias staticMap2 = AliasSeq!();
    static foreach (i; 0 .. firsts.length) {
        staticMap2 = AliasSeq!(staticMap2, fun!(firsts[i], seconds[i]));
    }
}

// An example struct with two template parameters
struct S(T, size_t length) {
}

// An example template that creates instantiations of the S template
// (This can be generalized to instantiation of any template.)
template Instantiate(T, size_t length) {
    alias Instantiate = S!(T, length);
}

// An example use
alias myTypes = AliasSeq!(int, double, long);
alias mySizes = AliasSeq!(1, 2, 3);
alias result = staticMap2!(Instantiate, myTypes, mySizes);

pragma(msg, result);

void main() {
}

Ali

February 06, 2023
On Monday, 6 February 2023 at 09:17:07 UTC, Ali Çehreli wrote:
> I adapted staticMap's implementation to two sets of arguments:

Thanks Ali, that's perfect. I thought of splitting the args in half a few hours later but hadn't got around to trying it.
February 08, 2023

On Monday, 6 February 2023 at 09:17:07 UTC, Ali Çehreli wrote:

>

I adapted staticMap's implementation to two sets of arguments:

So I've got this implementation, but wonder if I can generalise the arg splitting portion rather than write it manually for each N?

template staticMapN(size_t N, alias fun, args...) if (args.length % N == 0) {
  alias staticMapN = AliasSeq!();
  static foreach (i; 0 .. args.length / N)
    static if (N == 1)
      staticMapN = AliasSeq!(staticMapN, fun!(args));
    else static if (N == 2)
      staticMapN = AliasSeq!(staticMapN, fun!(args[0 .. $ / N][i], args[$ / N .. ($ / N) * 2][i]));
    else static if (N == 3)
      staticMapN = AliasSeq!(staticMapN, fun!(args[0 .. $ / N][i], args[$ / N .. ($ / N) * 2][i], args[($ / N) * 2 .. ($ / N) * 3][i]));
    // etc
}
February 09, 2023
On 2/8/23 12:04, John Chapman wrote:

> rather than write it manually for each N?

import std.meta : AliasSeq;

template pickArgs(size_t totalElements,
                  size_t argsPerElement,
                  size_t whichElement,
                  args...) {
    alias pickArgs = AliasSeq!();
    static foreach (a; 0 .. argsPerElement) {
        pickArgs = AliasSeq!(pickArgs, args[whichElement + a * totalElements]);
    }
}

template staticMapN(size_t N, alias fun, args...)
{
    static assert(N != 0, "N must be non-zero.");
    static assert((args.length % N) == 0,
                  "Mismatched number of arguments");

    enum totalElements = args.length / N;

    alias staticMapN = AliasSeq!();
    static foreach (e; 0 .. totalElements) {
        staticMapN = AliasSeq!(staticMapN,
                               fun!(pickArgs!(totalElements, N, e, args)));
    }
}

// An example struct with some template parameters
struct S(T, size_t length, size_t foo, size_t bar) {
}

// An example template that creates instantiations of the S template
template Instantiate(T, size_t length, size_t foo, size_t bar) {
    alias Instantiate = S!(T, length, foo, bar);
}

// Compile-time argument sets for three instantiations of the S template
alias myTypes = AliasSeq!(int, double, long);
alias mySizes = AliasSeq!(1, 2, 3);
alias myFoos = AliasSeq!(42, 43, 44);
alias myBars = AliasSeq!(100, 200, 300);

// A test with those 4 sets of template arguments
alias result = staticMapN!(4, Instantiate, myTypes, mySizes, myFoos, myBars);
pragma(msg, result);

void main() {
}

I could not figure out eliminating the hard-coded 4. Can we introspect the parameter list of a template like 'fun' in the example? If we could, then we could get 4 that way.

Ali

February 09, 2023

On Thursday, 9 February 2023 at 19:17:55 UTC, Ali Çehreli wrote:

>

I could not figure out eliminating the hard-coded 4. Can we introspect the parameter list of a template like 'fun' in the example? If we could, then we could get 4 that way.

Thank you for this. I don't mind hard-coding the N argument. TemplateArgsOf needs an instantiated template but maybe enum N = count(fun.stringof, ',') + 1 is good enough.

February 09, 2023
On 2/9/23 12:45, John Chapman wrote:
> On Thursday, 9 February 2023 at 19:17:55 UTC, Ali Çehreli wrote:
>> I could not figure out eliminating the hard-coded 4. Can we introspect
>> the parameter list of a template like 'fun' in the example? If we
>> could, then we could get 4 that way.
>
> Thank you for this. I don't mind hard-coding the N argument.
> TemplateArgsOf needs an instantiated template but maybe ```enum N =
> count(fun.stringof, ',') + 1``` is good enough.

Hopefully but very fragile:

template t(int[] T = [ 1, 2 ]) {}

void main() {
    import std.algorithm : count;
    enum N = count(t.stringof, ',') + 1;
    static assert(N == 1); // FAILS
}

Same thing with any other thing with comma in it e.g.

template t(T = anotherTemplate!(1, 2)) {}

Ali