strange CFTE issue

ag0aep6g via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun Apr 9 09:44:41 PDT 2017


On 04/09/2017 04:36 PM, Boris-Barboris wrote:
> Hello, I have a similar problem. For the life of me I can't make CTFE
> work while manipulating collections. Source:

This post is only loosely related to the thread. Generally, please make 
a new thread for a new problem.

> import std.meta;
> import std.traits;
>
> template isFunctionField(OwnerType, string field_name)
> {
>     enum isFunctionField =
>         isFunction(mixin(OwnerType.stringof ~ "." ~ field_name));

Aside: You forgot an exclamation mark here. isFunction is a template, 
not a function.

> }
>
> string[] sfilter(T)(string[] fields)
> {
>     string[] result;
>     foreach (f; fields)
>     {
>         enum isfunc = isFunctionField!(T, f);
> // variable f cannot be read at compile time.
> // Even when I change isFunctionField from template to function that
> returns
> // bool and takes "string field_name".
>         if (isfunc)
>             result ~= f;
>     }
>     return result;
> }
>
> string[] TypeFields(T)() pure
> {
>     enum field_names = [__traits(allMembers, T)];
>     pragma(msg, typeof(field_names)); // prints string[]
>     pragma(msg, field_names);         // prints correct member names
>     enum filtered = sfilter!(T)(field_names);
>     return filtered;
> }
>
> //then I call it by:
> enum fields = TypeFields!(SomeStruct)();
>
>   I can get string array of class members allright. Whenever I try to do
> any logic with it, I fail. I get that "enum field_names = " is not
> lvalue.

As far as I see, lvalue vs rvalue doesn't matter here. It's compile-time 
constant vs variable. Or maybe "static value" vs "dynamic value".

> How should I pass it to sfilter?

Via a template parameter. Function parameters are never considered 
static values (or compile-time constants), even during CTFE. It's 
important to understand and to keep in mind that CTFE follows the exact 
same rules as normal run-time evaluation (plus some more restrictions 
that apply only to CTFE). You can't define an enum with true run-time 
data, so you can't define it with a variable in CTFE either.

You can make `fields` a template sequence parameter [1]:

----
string[] sfilter(T, fields ...)()
{
     /* ... body as you have it ... */
}
----

Or you can make it a template value parameter [2] with type `string[]`:

----
string[] sfilter(T, string[] fields)()
{
     string[] result;
     foreach (f; aliasSeqOf!fields)
     {
         /* ... loop body as you have it ... */
     }
     return result;
}
----

Both times, the `foreach` operates on a compile-time list [3] (aka alias 
sequence aka AliasSeq). This is a special case of `foreach` dubbed 
"static foreach". It means that the loop variable `f` is a static value. 
It can be passed as a template argument.

As an alternative to the "static foreach", you could use recursion. And 
then you could make it just a template instead of a function template. 
CTFE wouldn't be involved. That's how std.meta.Filter [4] works. You can 
use it instead of writing your own filter function/template:

----
     enum field_names = __traits(allMembers, T);
     enum pred(string field_name) = isFunctionField!(T, field_name);
     enum filtered = [Filter!(pred, field_names)];
----

> When I change foreach loop into
> for loop, I can't index "string[] fields" array since "variable fields
> cannot be read at compile time".

Unlike `foreach`, `for` does not have a "static for" special case. The 
loop variable of a `for` loop is always dynamic, never static. You can't 
ever use it in a context that needs a static value.

>   Should I pass field_names string array as some other type, AliasSeq or
> something?

Aside: AliasSeq is not a type.

>   The thing is, on some level inside TypeFields, I will need to mutate
> array, I can't stick to tuples.

It's not obvious to me why this rules out compile-time lists (aka 
tuples). Note that you can convert both ways between a compile-time list 
(aka tuple) and an enum array. aliasSeqOf goes one way, wrapping in 
square brackets goes the other.

> I tried declaring array parameters
> immutable, but CTFE forbids casting from mutable into immutable. I'm
> kinda out of ideas.

I suppose you tried `immutable` to tell the compiler that the values are 
constant? But `immutable` doesn't mean "compile-time constant". 
Dynamically computed values can be immutable. Function parameters are 
never usable in static contextsZ.


[1] https://dlang.org/spec/template.html#variadic-templates
[2] https://dlang.org/spec/template.html#template_value_parameter
[3] https://dlang.org/ctarguments.html


More information about the Digitalmars-d-learn mailing list