Unless I'm missing something, it seems that neither of these are actually possible.

Consider an object which needs internal state to function.
The obvious answer is to create it in the constructor:

struct Foo(T)
{
        T* payload;
        
        this() { payload = cast(T*)malloc(T.sizeof); }
        ~this() { free(payload); }
        
        void foo() {
                // do something with payload that fails if not initialized
        }
}

But this is not possible in D, because structs can't have default constructors.

So one may think, I can use lazy initialization instead:

struct Foo(T)
{
        T* _payload;
        
        ~this() { if(_payload) free(_payload); }
        
        @property T* payload() const {
                if(!_payload)
                        (cast(Foo!T*)&this).payload = cast(T*)malloc(T.sizeof);
                
                return _payload;
        }
        
        void foo() {
                T* p = payload();
                // do something with payload that fails if not initialized
        }
        
        void bar() const {
                T* p = payload();
                // do something with payload that fails if not initialized
        }
}

So in C++, the above would be fine.
Since payload can never be perceived by the caller as uninitialized, the fact that it "breaks" const is irrelevant.

But you can't do this in D.

If the object is defined at module scope as shared static immutable, the compiler may put it in a readonly section of the executable which would cause an access violation upon trying to initialize it, and there is no way to prevent this from happening.

I'm hoping someone will tell me I'm wrong here, because the only alternative to the above approaches is to add boilerplate to _every_ _single_ _function_ that uses the payload in order to deal with separate cases where it's uninitialized.

Is there really no solution for this?

Reply via email to