Thread overview
Manipulating variables without evaluating them
May 07, 2023
Dadoum
May 07, 2023
Dadoum
May 08, 2023
ag0aep6g
May 08, 2023
Salih Dincer
May 08, 2023
Dadoum
May 08, 2023
ag0aep6g
May 08, 2023
Dadoum
May 07, 2023

Hello,

I am currently making a wrapper around libplist in D, and to get a good syntax I first thought about making a pl function with bunch of overloads allowing to convert strings, integers, booleans into the corresponding Plist type. It is working but the problem here is that associative array literals are not preserving order (they are immediately hashed, and so all the keys are in a random order).

To overcome that, I create a template plistDict as such:

template plistDict(alias U) {
    PlistDict pl() {
        auto dict = new PlistDict();
        static foreach (elem; dict) {
            dict[elem.key] = elem.value;
        }
        return dict;
    }
}

The static foreach ensures everything is running at compile time, and so no hash should mess with the keys. But the problem here is that it tries to evaluate at compile time the value of each entry in the associative array (what it can't do since it's a wrapper around a C library!).

The same kind of issue can be encountered in this small code snippet:

string crashingFunction() {
	assert(!__ctfe);
	return "OK";
}

template macroLikeTemplate(alias U) {
	string macroLikeTemplate() {
		return "" ~ U;
	}
}

void main()
{
	macroLikeTemplate!(crashingFunction());
}

Here, the function is evaluated before the element is passed to the template, and is failing (here because of an assert, but we can imagine something that would fail only at compile-time like a C call), while the substitution could just work fine if it wasn't evaluated before hand.

Is there any way to make this work?

May 07, 2023

On Sunday, 7 May 2023 at 17:11:52 UTC, Dadoum wrote:

>
string crashingFunction() {
	assert(!__ctfe);
	return "OK";
}

template macroLikeTemplate(alias U) {
	string macroLikeTemplate() {
		return "" ~ U;
	}
}

void main()
{
	macroLikeTemplate!(crashingFunction());
}

By the way, this snippet could work if I just made macroLikeTemplate a regular function. But in the case I presented precisely, I have to iterate at compile time through the elements, otherwise the elements would be put in the wrong order, thus it can't be a function at runtime.

May 08, 2023
On 07.05.23 19:11, Dadoum wrote:
> To overcome that, I create a template `plistDict` as such:
> 
> ```d
> template plistDict(alias U) {
>      PlistDict pl() {
>          auto dict = new PlistDict();
>          static foreach (elem; dict) {
>              dict[elem.key] = elem.value;
>          }
>          return dict;
>      }
> }
> ```

1. The `static foreach` can't work. `dict` is a run-time value. You can't iterate it at compile time like that.
2. `U` is unused.
3. You're just assigning everything from `dict` back to `dict`.

Did you maybe mean `static foreach (elem; U)`?

> The `static foreach` ensures everything is running at compile time, and so no hash should mess with the keys. But the problem here is that it tries to evaluate at compile time the value of each entry in the associative array (what it can't do since it's a wrapper around a C library!).

Hard to tell what's going on because your code is missing crucial parts: what is `PlistDict` and how are you instantiating `plistDict`?

> The same kind of issue can be encountered in this small code snippet:
> 
> ```d
> string crashingFunction() {
>      assert(!__ctfe);
>      return "OK";
> }
> 
> template macroLikeTemplate(alias U) {
>      string macroLikeTemplate() {
>          return "" ~ U;
>      }
> }
> 
> void main()
> {
>      macroLikeTemplate!(crashingFunction());
> }
> 
> ```

This one is a simple fix. Just don't call `crashingFunction` at the instantiation site:

    macroLikeTemplate!(crashingFunction);
May 08, 2023

On Sunday, 7 May 2023 at 17:11:52 UTC, Dadoum wrote:

>

Hello,

I am currently making a wrapper around libplist in D, and to get a good syntax I first thought about making a pl function with bunch of overloads allowing to convert strings, integers, booleans into the corresponding Plist type. It is working but the problem here is that associative array literals are not preserving order (they are immediately hashed, and so all the keys are in a random order).

It's hard to figure out what you want but there are errors in your code. This works:

auto plistDict(alias AA)()
{
  auto dict = new string[AA.length];
  foreach (key, value; AA)
  {
    dict[key] = value;
  }
  return dict;
}

import std.stdio;

void main()
{
  auto plist = plistDict!([0: "zero",
                           1: "one",
                           2: "two"]);
  plist.writeln; // ["zero", "one", "two"]
}

And yes, the data is not converted at compile time.

SDB@79

May 08, 2023

On Monday, 8 May 2023 at 11:06:59 UTC, Salih Dincer wrote:

>

On Sunday, 7 May 2023 at 17:11:52 UTC, Dadoum wrote:

>

Hello,

I am currently making a wrapper around libplist in D, and to get a good syntax I first thought about making a pl function with bunch of overloads allowing to convert strings, integers, booleans into the corresponding Plist type. It is working but the problem here is that associative array literals are not preserving order (they are immediately hashed, and so all the keys are in a random order).

It's hard to figure out what you want but there are errors in your code. This works:

auto plistDict(alias AA)()
{
  auto dict = new string[AA.length];
  foreach (key, value; AA)
  {
    dict[key] = value;
  }
  return dict;
}

import std.stdio;

void main()
{
  auto plist = plistDict!([0: "zero",
                           1: "one",
                           2: "two"]);
  plist.writeln; // ["zero", "one", "two"]
}

And yes, the data is not converted at compile time.

SDB@79

I should have worded my question differently. The problem here is that the values are not constant at compile time. It should be emulated with

auto plistDict(alias AA)()
{
	auto dict = new string[AA.length];
	foreach (key, value; AA)
	{
		dict[key] = value;
	}
	return dict;
}

import std.stdio;

extern(C) void functionThatCannotBeEvaluatedAtCompileTime();

string conversionFunction(string val) {
    functionThatCannotBeEvaluatedAtCompileTime();
    return val;
}

void main()
{
	auto plist = plistDict!([
		0: "zero".conversionFunction, // Error: `functionThatCannotBeEvaluatedAtCompileTime` cannot be interpreted at compile time, because it has no available source code
		1: "one".conversionFunction,
		2: "two".conversionFunction,
	]);
	plist.writeln;
}

Here, it complains that the values cannot be evaluated since it has no source code. But I don't care about the source code here, I just want the code on the left to be executed at runtime, not evaluate it right at compilation.

(and yeah in my first snippet I shouldn't iterate through the runtime dict but the compile time U, I made a mistake while compacting the code to post it here).

Is there a way to use the associative array here at compile time while avoiding the evaluation of those keys?

May 08, 2023

On Monday, 8 May 2023 at 11:42:44 UTC, Dadoum wrote:

>
auto plistDict(alias AA)()
{
	auto dict = new string[AA.length];
	foreach (key, value; AA)
	{
		dict[key] = value;
	}
	return dict;
}

import std.stdio;

extern(C) void functionThatCannotBeEvaluatedAtCompileTime();

string conversionFunction(string val) {
    functionThatCannotBeEvaluatedAtCompileTime();
    return val;
}

void main()
{
	auto plist = plistDict!([
		0: "zero".conversionFunction, // Error: `functionThatCannotBeEvaluatedAtCompileTime` cannot be interpreted at compile time, because it has no available source code
		1: "one".conversionFunction,
		2: "two".conversionFunction,
	]);
	plist.writeln;
}

[...]

>

Is there a way to use the associative array here at compile time while avoiding the evaluation of those keys?

Instead of passing values, pass functions that return the values:

	auto plist = plistDict!([
		0: () => "zero".conversionFunction,
		1: () => "one".conversionFunction,
		2: () => "two".conversionFunction,
	]);

And in plistDict, call the functions:

	static foreach (key, fun; AA)
	{
		dict[key] = fun();
	}
May 08, 2023

On Monday, 8 May 2023 at 12:01:35 UTC, ag0aep6g wrote:

>

Instead of passing values, pass functions that return the values:

	auto plist = plistDict!([
		0: () => "zero".conversionFunction,
		1: () => "one".conversionFunction,
		2: () => "two".conversionFunction,
	]);

And in plistDict, call the functions:

	static foreach (key, fun; AA)
	{
		dict[key] = fun();
	}

Thanks, I can get further now with this trick, but now it is complaining that some are delegate and other are function pointer because in the real situation this happens:

	auto two = "two";
	auto plist = plistDict!([
		0: () => "zero".conversionFunction,
		1: () => "one".conversionFunction,
		2: () => two.conversionFunction,
	]);