June 20, 2017
Another meta-programming question ;-)

Inside a variadic function template that takes a list of arguments, I want to call another function and pass it the arguments run through another function. In C++ it would look like this:

template<typename T>
T double_int(T val) { return val; }

int double_int(int val) { return 2 * val; }

template<typename... T>
void double_ints(void F(T...), T... args) {
    F(double_int(args)...);
}

void test(const char* str, int val) {
    cout << str << " = " << val << "\n";
}

int main() {
    double_ints(test, "1 + 2", 3); // 1 + 2 = 6
    return 0;
}

I tried this in D:

void test(string name, int val)
{
  writefln("%s = %d", name, val);
}

template DoubleInt(A...)
{
  static if (A.length == 0)
    alias DoubleInt = AliasSeq!();
  else static if (is(typeof(A[0]) == int))
    alias DoubleInt = AliasSeq!(2 * A[0], DoubleInt!(A[1..$]));
  else
    alias DoubleInt = AliasSeq!(A[0], DoubleInt!(A[1..$]));
}

void double_ints(F, A...)(F f, A args)
{
  f(DoubleInt!args);
}

void main()
{
  double_ints(&test, "1 + 2", 3);
}

...but ldc2 complains that "variable __args_field_1 cannot be read at compile time". Yeah, I understand...

I also tried a solution similar to C++:

A DoubleInt(A)(A a)
  if (!is(typeof(A) == int))
  {
    return a;
  }

A DoubleInt(A)(A a)
  if (is(typeof(A) == int))
  {
    return 2 * a;
  }

void double_ints(F, A...)(F f, A args)
{
  f(DoubleInt!(args));
}

...but I get: "template instance callas.double_ints!(void function(string, int), string, int) error instantiating". The difference with C++ is that I don't get to control the expansion of the argument pack: "f(DoubleInt!(args))" is interpreted as "f(DoubleInt!(args...))", not "f(DoubleInt!(args))...".

Any ideas? Thanks...





June 20, 2017
On 06/20/2017 10:28 PM, Jean-Louis Leroy wrote:
> Any ideas? Thanks...

1) You can foreach over args:

----
void test(string name, int val)
{
  import std.stdio;
  writefln("%s = %d", name, val);
}

T double_int(T)(T val) { return val; }
int double_int(int val) { return 2 * val; }

void double_ints(F, A...)(F f, A args)
{
  foreach (ref arg; args) arg = double_int(arg);
  f(args);
}

void main()
{
  double_ints(&test, "1 + 2", 3);
}
----

2) Or you could make a generic `tuple_map` function and use that:

----
/* ... test, double_int, and main as above ... */

auto tuple_map(alias fun, T ...)(T things)
{
    import std.typecons: Tuple, tuple;
    static if (things.length == 0) return tuple();
    else
    {
        auto head = fun(things[0]);
        auto tail = tuple_map!fun(things[1 .. $]);
        return tuple(head, tail.expand);
    }
}

void double_ints(F, A...)(F f, A args)
{
  f(tuple_map!double_int(args).expand);
}
----