Search code examples
d

scoped!T() not working for class members


I am trying to use the scoped!T() template to allocate inside the Program class to save on an allocation. I can't get it working with the following exception.

Error: cannot cast &Scoped([void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void, void]).Scoped_store to ulong at compile time

I'm not shure if it is by design that one cannot use scoped for a class member or if it is an erorr in the scoped template. If its by design than this is a really crappy error message by the way.

The following code is to demonstrate my problem.

import std.typecons;

public class Foo
{
    int i = 0;
}

public class Program
{
    auto myFoo = scoped!Foo();
}

void main(string[] argv)
{
    new Program();
}

Solution

  • auto myFoo = scoped!Foo();
    

    This line will attempt to instantiate a Foo class and initialize the myFoo field during compilation. This is because scoped!Foo() is interpreted as a default value, which are always computed during compilation, therefore the compiler attempts to interpret this expression (CTFE). Because scoped does some low-level things to work, CTFE doesn't work in this case.

    Instead, you should initialize the field at runtime, during class construction:

    public class Program
    {
        typeof(scoped!Foo()) myFoo;
    
        this()
        {
            myFoo = scoped!Foo();
        }
    }
    

    The scoped documentation actually covers this case. Quoting it:

    Scoped member variables must have type typeof(scoped!Class(args)), and be initialized with a call to scoped. See below for an example.

    And here is the referenced example:

    class A
    {
        int x;
        this()     {x = 0;}
        this(int i){x = i;}
        ~this()    {}
    }
    
    // ...
    
    // Use as member variable
    struct B
    {
        typeof(scoped!A()) a; // note the trailing parentheses
    
        this(int i)
        {
            // construct member
            a = scoped!A(i);
        }
    }