Jump to page: 1 2
Thread overview
April 07

I'm making a modification to a D binding for a C library. I made a CTFE function which takes a function declaration with one or more const(char)* or char* parameters and makes an overload that accepts D strings. While this function is only used during compile time, unfortunately, I have found that it results in the library no longer supporting -betterC. The compiler gives the following error.

/usr/include/dlang/dmd/core/lifetime.d(2760,42): Error: `TypeInfo` cannot be used with -betterC
/usr/include/dlang/dmd/std/utf.d(1556,24):        instantiated from here: `_d_newclassT!(UTFException)`
/usr/include/dlang/dmd/std/utf.d(1563,32):        instantiated from here: `exception!(const(char)[])`
/usr/include/dlang/dmd/std/utf.d(1186,54):        instantiated from here: `decodeImpl!(true, Flag.no, const(char)[])`
/usr/include/dlang/dmd/std/range/primitives.d(2551,18):        instantiated from here: `decode!(Flag.no, const(char)[])`
/usr/include/dlang/dmd/std/range/primitives.d(178,40):        instantiated from here: `front!char`
Error /usr/bin/dmd failed with exit code 1.

I don't know what part of the function uses TypeInfo (as I don't really understand what that is), but I know that it is this CTFE function causing the error, because commenting out the mixins that use it results in the -betterC program compiling properly. Here is the CTFE function I wrote:

string MakeStringOverload(alias func)() {
    string def = ReturnType!func.stringof ~" "~ __traits(identifier, func) ~ "(";

    auto paramNames = ParameterIdentifierTuple!func;

    import std.algorithm.searching;
    foreach(i, paramType; Parameters!func) {
        if (paramType.stringof.canFind("char")) def ~= "ref string"; // Made a reference so that D string literals know to use the base version of the function and not this one.
        else def ~= paramType.stringof;
        def ~= " "~paramNames[i] ~ ", ";
    }
    def.length -= 2;
    def ~= ") { ";
    if (ReturnType!func.stringof != "void") def ~= "return ";
    def ~= __traits(identifier, func) ~ "(";
    foreach(i, argument; paramNames) {
        if (Parameters!func[i].stringof.canFind("char")) def ~= "cast(char*)";
        def ~= argument ~ ", ";
    }
    def.length -= 2;
    def ~= "); }";

    return def;
}

In the module where the base versions of functions are declared, I do the following to generate overloads:

mixin(MakeStringOverload!SetWindowTitle);
mixin(MakeStringOverload!LoadShader);
mixin(MakeStringOverload!LoadShaderFromMemory);
mixin(MakeStringOverload!GetShaderLocation);

So what part of my function is using "TypeInfo"? Can I somehow label my function as compile-time only so that it stops complaining? If not what else can I do to get it to stop complaining and compile with betterC?

April 07
Unfortunately runtime and CTFE are the same target in the compiler.

So that function is being used for both, and hence uses GC (appending).

```d
template Foo(Args) {
	enum Foo = () {
		return Args.init;
	}();
}
```

Something like that should work instead.
April 07

On Sunday, 7 April 2024 at 08:59:55 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

Unfortunately runtime and CTFE are the same target in the compiler.

:-(
Will this ever be changed?

>
template Foo(Args) {
	enum Foo = () {
		return Args.init;
	}();
}

Something like that should work instead.

I'm sorry, but I can't comprehend any of your example. What would be fed into Args? I don't understand how this works, or how I would use it for what I want.

April 08
On 08/04/2024 10:45 AM, Liam McGillivray wrote:
> On Sunday, 7 April 2024 at 08:59:55 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> Unfortunately runtime and CTFE are the same target in the compiler.
> 
> :-(
> Will this ever be changed?

A tad unlikely, it would be a rather large change architecturally to dmd.

>> ```d
>> template Foo(Args) {
>>     enum Foo = () {
>>         return Args.init;
>>     }();
>> }
>> ```
>>
>> Something like that should work instead.
> 
> I'm sorry, but I can't comprehend any of your example. What would be fed into `Args`? I don't understand how this works, or how I would use it for what I want.

You would replace it with whatever template parameters you want (including nothing). It's there as a place holder.

Same for the return on the closure.

But the main thing to understand is that the closure that gives the enum a value, that'll be CTFE only, no runtime target.
April 08

On Monday, 8 April 2024 at 08:12:22 UTC, Richard (Rikki) Andrew Cattermole wrote:

> > >
template Foo(Args) {
    enum Foo = () {
        return Args.init;
    }();
}

Something like that should work instead.

I'm sorry, but I can't comprehend any of your example. What would be fed into Args? I don't understand how this works, or how I would use it for what I want.

You would replace it with whatever template parameters you want (including nothing). It's there as a place holder.

Same for the return on the closure.

But the main thing to understand is that the closure that gives the enum a value, that'll be CTFE only, no runtime target.

Are you saying that this is a way to guarantee that the code is compile-time only?

I still understand very little of this code. I'm not experienced in D metaprogramming; just the function I posted above was a major achievement for me. I don't understand how I would use the code you gave in place of the function I have written and posted above.

When you say that "You would replace it with whatever template parameters you want", are you saying that instead of doing mixin(MakeStringOverload!SetWindowTitle); mixin(MakeStringOverload!LoadShader); as posted above, I would write mixin(Foo!(SetWindowTitle, LoadShader));?

What does the return Args.init; line mean in your example? Am I supposed to replace this with a call to the CTFE function I had already written? If so, it didn't work. Making such a replacement resulted in the same "TypeInfo" error that I had already.

April 09
On 09/04/2024 11:42 AM, Liam McGillivray wrote:
> On Monday, 8 April 2024 at 08:12:22 UTC, Richard (Rikki) Andrew Cattermole wrote:
>>>> ```d
>>>> template Foo(Args) {
>>>>     enum Foo = () {
>>>>         return Args.init;
>>>>     }();
>>>> }
>>>> ```
>>>>
>>>> Something like that should work instead.
>>>
>>> I'm sorry, but I can't comprehend any of your example. What would be fed into `Args`? I don't understand how this works, or how I would use it for what I want.
>>
>> You would replace it with whatever template parameters you want (including nothing). It's there as a place holder.
>>
>> Same for the return on the closure.
>>
>> But the main thing to understand is that the closure that gives the enum a value, that'll be CTFE only, no runtime target.
> 
> Are you saying that this is a way to guarantee that the code is compile-time only?

More or less.

> I still understand very little of this code. I'm not experienced in D metaprogramming; just the function I posted above was a major achievement for me. I don't understand how I would use the code you gave in place of the function I have written and posted above.

Let's break it down.

The expression that initializes the ``func`` variable, this is a closure. The ``a`` and ``b`` are function parameter names (types do not need to be provided).

```d
alias FuncType = int function(int, int);

FuncType func = (a, b) {
	return a + b;
};

int value = func(1, 2);
```

The alias is used to give a name to the function pointer type.

Next, let's combine the function pointer storage variable with the result with the call.

```d
int value = (a, b) {
	return a + b;
}(1, 2);
```

We can swap the type ``int`` for the ``enum`` keyword, this produces a compile time constant that is an ``int`` that has the value 3.

```d
enum Value = (a, b) {
	return a + b;
}(1, 2);
```

This alone should be a CTFE only function.

But if we want template parameters, we'd need to wrap it with the template.

```d
template Value(int a, int b) {
	enum Value = () {
		return a + b;
	}();
}

int value = Value!(1, 2);
```

Does that help?
April 09

On Tuesday, 9 April 2024 at 00:02:02 UTC, Richard (Rikki) Andrew Cattermole wrote:

>
enum Value = (a, b) {
	return a + b;
}(1, 2);

This alone should be a CTFE only function.

But if we want template parameters, we'd need to wrap it with the template.

template Value(int a, int b) {
	enum Value = () {
		return a + b;
	}();
}

int value = Value!(1, 2);

Does that help?

I had to reread this a few times to get a sense of what this is. I might have just got it. This is effectively a CTFE function for generating a constant based on the sum of two numbers, right? Doing int value = Value!(1, 2); would set value to 3, right?

I suppose this was a good new thing to learn, though I'm still quite far from being able to construct a function from another function using a template.

I suppose that if I wanted it to make a function from another function, I may be able to do it in a template using some static foreach to make arrays of function parameters, and then combine them together without the use of strings, instead using placeholders (aliases or whatever they'd be called) and maybe the tupleof function. Am I headed in the right direction (if you can understand my weak attempt to describe the direction I'm thinking of going in)?

April 09

On Sunday, 7 April 2024 at 06:46:39 UTC, Liam McGillivray wrote:

>

instantiated from here: front!char

Looks like autodecoding, try to comment canFind.

April 10
On 09/04/2024 12:48 PM, Liam McGillivray wrote:
> On Tuesday, 9 April 2024 at 00:02:02 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> ```d
>> enum Value = (a, b) {
>>     return a + b;
>> }(1, 2);
>> ```
>>
>> This alone should be a CTFE only function.
>>
>> But if we want template parameters, we'd need to wrap it with the template.
>>
>> ```d
>> template Value(int a, int b) {
>>     enum Value = () {
>>         return a + b;
>>     }();
>> }
>>
>> int value = Value!(1, 2);
>> ```
>>
>> Does that help?
> 
> I had to reread this a few times to get a sense of what this is. I might have just got it. This is effectively a CTFE function for generating a constant based on the sum of two numbers, right? Doing `int value = Value!(1, 2);` would set `value` to 3, right?

Yes.

> I suppose this was a good new thing to learn, though I'm still quite far from being able to construct a function from another function using a template.
> 
> I suppose that if I wanted it to make a function from another function, I may be able to do it in a template using some `static foreach` to make arrays of function parameters, and then combine them together without the use of strings, instead using placeholders (aliases or whatever they'd be called) and maybe the `tupleof` function. Am I headed in the right direction (if you can understand my weak attempt to describe the direction I'm thinking of going in)?

``tupleof`` isn't a function, its a property to get a "tuple" a sequence of fields for a struct/class.

However most likely you'd have to resort to string mixins if you're messing about with parameters like I think? you are asking for.

I'm not entirely sure what you're wanting there.
April 09

On Sunday, 7 April 2024 at 08:59:55 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

Unfortunately runtime and CTFE are the same target in the compiler.

So that function is being used for both, and hence uses GC (appending).

Are you sure that string appending was really the problem that caused the "TypeInfo" build error? I forgot about this, but I had already had a working CTFE function with string appending before adding the new one that lead to this error. The symbols that it generates could be used in the program compiled with betterC.

string EnumPrefixes(T)(string oldName, string prefix) {
    string result = "enum " ~ oldName ~ " {\n";
    static foreach(member; __traits(allMembers, T)) {
        result ~= "    " ~ prefix ~ member ~ " = " ~ __traits(getMember, T, member).to!int.to!string ~ ",\n";
    }
    return result ~ "}\n";
}

The purpose of this was that the enums used by the C library were too verbose. I had changed them from things like KeyboardKey.KEY_C to Key.C. I wanted to leave the new enums written directly in the module since these were recommended for use, but then generate the old ones with CTFE for backwards compatibility. The function above was used like mixin(EnumPrefixes!Key("KeyboardKey", "KEY_"));, and the compiler would allow it even when building with betterC.

« First   ‹ Prev
1 2