Thread overview
Scripting with Variant from std.variant: parameter passing
Feb 02, 2024
Carl Sturtivant
Feb 02, 2024
Danilo
Feb 02, 2024
Anonymouse
Feb 03, 2024
Danilo
Feb 03, 2024
Anonymouse
Feb 02, 2024
Carl Sturtivant
Feb 02, 2024
Paul Backus
Feb 03, 2024
Carl Sturtivant
February 02, 2024

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

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

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

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

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

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

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

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

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.