Thread overview
Scripting with Variant from std.variant: parameter passing
Feb 02
Danilo
Feb 03
Danilo
February 02

It seems I cannot pass e.g. an int argument to a Variant function parameter. What's the simplest way to work around this restriction?

February 02

On Friday, 2 February 2024 at 08:22:42 UTC, Carl Sturtivant wrote:

>

It seems I cannot pass e.g. an int argument to a Variant function parameter. What's the simplest way to work around this restriction?

Just tell the compiler clearly what you want.

import std;

void f(Variant x) {
    writeln(x);
}

void main() {
    f( Variant(42)    );
    f( Variant(2.5)   );
    f( Variant("Hi!") );
}
February 02

On Friday, 2 February 2024 at 08:22:42 UTC, Carl Sturtivant wrote:

>

It seems I cannot pass e.g. an int argument to a Variant function parameter. What's the simplest way to work around this restriction?

The easiest thing would be to actually pass it a Variant with someFunction(Variant(myInt)).

The more-involved thing would be to write a template constrained to non-Variants that does the above for you.

auto someFunction(T)(T t)
if (!is(T : Variant))
{
    return someFunction(Variant(t));
}

auto someFunction(Variant v)
{
    // ...
}

void main()
{
    someFunction(42);
    someFunction("hello");
    someFunction(3.14f);
    someFunction(true);
    someFunction(Variant(9001));
}
February 02

On Friday, 2 February 2024 at 08:22:42 UTC, Carl Sturtivant wrote:

>

It seems I cannot pass e.g. an int argument to a Variant function parameter. What's the simplest way to work around this restriction?

You'd have to implement the function that accepts the parameters and wraps in a Variant.

This is the best I can come up with, which should be copy/pasteable to other shims:

void foo(Variant x, Variant y) { ... }

import std.meta : allSatisfy;

enum isVariant(T) = is(T == Variant);

// this is going to suck at CTFE but...
string argsAsVariants(size_t count)
{
   import std.format;
   import std.range;
   import std.alglorithm;
   import std.array;
   return iota(count).map!(i => format("Variant(args[%s])", i).join(",");
}

// shim
auto foo(Args...)(Args args) if (!allSatisfy!(isVariant, Args))
{
    mixin("return foo(", argsAsVariants(args.length), ");");
}

-Steve

February 02

On Friday, 2 February 2024 at 19:22:22 UTC, Steven Schveighoffer wrote:

>
void foo(Variant x, Variant y) { ... }

import std.meta : allSatisfy;

enum isVariant(T) = is(T == Variant);

// this is going to suck at CTFE but...
string argsAsVariants(size_t count)
{
   import std.format;
   import std.range;
   import std.alglorithm;
   import std.array;
   return iota(count).map!(i => format("Variant(args[%s])", i).join(",");
}

// shim
auto foo(Args...)(Args args) if (!allSatisfy!(isVariant, Args))
{
    mixin("return foo(", argsAsVariants(args.length), ");");
}

Thanks for this idea. I'll work on it.

>

-Steve

February 02

On Friday, 2 February 2024 at 20:28:50 UTC, Carl Sturtivant wrote:

>

On Friday, 2 February 2024 at 19:22:22 UTC, Steven Schveighoffer wrote:

>
// shim
auto foo(Args...)(Args args) if (!allSatisfy!(isVariant, Args))
{
    mixin("return foo(", argsAsVariants(args.length), ");");
}

Thanks for this idea. I'll work on it.

Another variation on the same theme:

void foo(Variant x, Variant y)
{
    import std.stdio: writeln;
    writeln("x = ", x);
    writeln("y = ", y);
}

/// map over a variadic argument list
template mapArgs(alias fun)
{
	auto mapArgs(Args...)(auto ref Args args)
	{
		import std.typecons: tuple;
		import core.lifetime: forward;
		import std.meta: Map = staticMap;

		auto ref mapArg(alias arg)()
		{
			return fun(forward!arg);
		}

		return tuple(Map!(mapArg, args));
	}
}

import std.variant: Variant;
import std.meta: allSatisfy;

enum isVariant(T) = is(T == Variant);

auto foo(Args...)(Args args)
    if (!allSatisfy!(isVariant, Args))
{
    return .foo(mapArgs!Variant(args).expand);
}

void main()
{
    foo(123, 456);
    foo("hello", "world");
}
February 03

On Friday, 2 February 2024 at 11:31:09 UTC, Anonymouse wrote:

>

On Friday, 2 February 2024 at 08:22:42 UTC, Carl Sturtivant wrote:

>

It seems I cannot pass e.g. an int argument to a Variant function parameter. What's the simplest way to work around this restriction?

The easiest thing would be to actually pass it a Variant with someFunction(Variant(myInt)).

The more-involved thing would be to write a template constrained to non-Variants that does the above for you.

auto someFunction(T)(T t)
if (!is(T : Variant))
{
    return someFunction(Variant(t));
}

auto someFunction(Variant v)
{
    // ...
}

void main()
{
    someFunction(42);
    someFunction("hello");
    someFunction(3.14f);
    someFunction(true);
    someFunction(Variant(9001));
}

To be honest, this doesn't make sense.

if (!is(T : Variant)) returns true for inputs like 42, "hello", 3.14f, but the input is not a Variant but a random type.

Yes, it's nice that it works in this case. It's just not logical, it doesn't make sense because 42 just simply isn't a Variant, it's an int.

February 03

On Saturday, 3 February 2024 at 08:04:40 UTC, Danilo wrote:

>

To be honest, this doesn't make sense.

if (!is(T : Variant)) returns true for inputs like 42, "hello", 3.14f, but the input is not a Variant but a random type.

Yes, it's nice that it works in this case. It's just not logical, it doesn't make sense because 42 just simply isn't a Variant, it's an int.

I read it several times but I don't think I understand what you mean.

The constraint if (!is(T : Variant)) is true for every input that is not a Variant, yes. The point of it is to let calls to someFunction(myVariant) resolve to the non-templated auto someFunction(Variant).

Is your argument that it's wrong to assume an int can be wrapped in a Variant, because it isn't one? That in turn doesn't make sense -- your example does the same, just explicitly.

void main() {
    f( Variant(42)    );
    f( Variant(2.5)   );
    f( Variant("Hi!") );
}

How is this different from the following?

void main() {
    (v){ f(Variant(v)); }(42);
    (v){ f(Variant(v)); }(2.5);
    (v){ f(Variant(v)); }("Hi!");
}

And how is that different from the following?

void main()
{
    auto g(T)(T t)
    {
        f(Variant(t));
    }

    g(42);
    g(2.5);
    g("Hi!");
}

Which is in what way different from the following?

auto g(T)(T t)
if (!is(T : Variant))
{
    return f(Variant(t));
}

auto f(Variant v)
{
    // ...
}

void main()
{
    g(42);
    g("hello");
    g(3.14f);
    g(true);
}

And how is that not the same as my original example?

February 03

On Friday, 2 February 2024 at 20:58:12 UTC, Paul Backus wrote:

>

Another variation on the same theme:

/// map over a variadic argument list
template mapArgs(alias fun)
{
	auto mapArgs(Args...)(auto ref Args args)
	{
		import std.typecons: tuple;
		import core.lifetime: forward;
		import std.meta: Map = staticMap;

		auto ref mapArg(alias arg)()
		{
			return fun(forward!arg);
		}

		return tuple(Map!(mapArg, args));
	}
}

import std.variant: Variant;
import std.meta: allSatisfy;

enum isVariant(T) = is(T == Variant);

auto foo(Args...)(Args args)
    if (!allSatisfy!(isVariant, Args))
{
    return .foo(mapArgs!Variant(args).expand);
}

Thanks, will study the library machinery you used here.