Search code examples
classpropertiesdgetter-setter

Is there a "deleter" in D?


Is there a way to implement a deleter in D? I could not find any references to that. In the following example, I want to use the deleter to reset the value of internal back to its original state. If there is no such thing as a deleter, then what would be the preferred and idiomatic way to achieve the above mentioned requirement? (And if that is the case, I'm also interested in the design choice, on why such type of property function is not part of the language?)

class Class
{
    private uint internal = 0;

    @property uint value()
    {
        return this.internal;
    }

    @property uint value(uint input)
    {
        return this.internal = input;
    }
}

UPDATE:

Based on the conversation below: I gave a dummy example, which led us to think, that this mechanism I'm looking for can easily be achieved via a simple setter and the usage of the .init value of the attribute. However that is not the case. What if this property is basically just a convenient wrapper around several function calls? Like a couple of C function invocations that resets the semantically related states -- which is exactly the case of mine.

To clarify even further, we are talking about a similar mechanism that Python has, which is supporting getter, setter and deleter.


Solution

  • Since I had never heard of a deleter before, I initially answered a somewhat different question than what was asked. D does not have anything like Python's deleters, and the below suggestion could not be called idiomatic, but it does essentially the same thing and looks somewhat similar.

    Note that this will lead to non-intuitive behavior for nullable types - a.value = null; will call the special deleter function, while a.value = cast(Foo*)null; will call the regular setter. So you probably shouldn't do this at all.

    A more idiomatic way would be to either have a separate clearValue() function or do automatic cleanup when the value is set to its 'deleted' value (be that null, 0 or whatever).

    class Class
    {
        private uint internal = 0;
    
        @property uint value()
        {
            return this.internal;
        }
    
        @property uint value(uint input)
        {
            return this.internal = input;
        }
    
        // deleter
        @property uint value(typeof(null) input)
        {
            DoSpecialCleanup();
            return this.internal = internal.init;
        }
    }
    
    unittest
    {
        auto c = new Class();
        c.value = 13;
        assert(c.value == 13);
        c.value = null; // Calling deleter here.
        assert(c.value == 0);
    }
    

    In my initial (mis)understanding of the question, I took it to mean a reset of all fields to their initial values. That could be done with the following code:

    import std.traits;
    
    void reset(T)(auto ref T t) if (is(T == Unqual!T)) {
        static if (is(T == class)) {
            auto data = T.classinfo.initializer[0..$];
    
            foreach (e; FieldNameTuple!T) {
                alias FieldType = typeof(__traits(getMember, T, e));
                enum offset = __traits(getMember, T, e).offsetof;
    
                static if (is(FieldType == Unqual!FieldType)) {
                    __traits(getMember, t, e) = *cast(FieldType*)data[offset..$];
                }
            }
        } else static if (!is(typeof(t = T.init))) {
            foreach (e; FieldNameTuple!T) {
                alias FieldType = typeof(__traits(getMember, T, e));
    
                static if (is(FieldType == Unqual!FieldType)) {
                    __traits(getMember, t, e) = __traits(getMember, T.init, e);
                }
            }
        } else {
            t = T.init;
        }
    }
    
    class Bar {
        align(16):
        int n = 3;
        const int n2 = 19;
    }
    
    struct Baz {
        align(16):
        int n = 3;
        const int n2 = 19;
    }
    
    unittest {
        Bar b = new Bar();
        b.n = 14;
        reset(b);
        assert(b.n == 3);
    
        Baz b2;
        b2.n = 14;
        reset(b2);
        assert(b2.n == 3);
    }