Thread overview
staticMap but with two arguments
February 06

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
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
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

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
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

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
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