On Tuesday, 7 May 2013 at 08:58:47 UTC, Dicebot wrote:
I really think that UDA annotations + single mixin like this:

class Test
{
    private:
        // annotated stuff
    public:
    mixin implementAnnotations!Test;
}

is a most type-safe approach which scales.

I think any method will scale(except for insane methods like inheriting from a templated class that receives the properties schema via template arguments...). After all - we are dealing with declarations here, and we can't stuck as many as we want while still keeping the code readable.

The problem with any solution that declares the fields directly in the struct\class's body is that the field identifier overshadows the property methods. If we look at your example, and declare a field to be made into a property:

    class Test
    {
        private:
            @Property int foo;
        public:
            mixin implementAnnotations!Test;
    }

Now, the expected behavior is that `implementAnnotations!Test` will declare a getter and a setter, both named `foo`. But even if it does that, it won't do us any good - because the accessors are declared inside a mixin, so they are overshadowed by the same `foo` we wanted to encapsulate!

So, we have to change it:

    class Test
    {
        private:
            @Property int m_foo;
        public:
            mixin implementAnnotations!Test;
    }

and inside `implementAnnotations` we would have to strip `m_foo` out of it's prefix to get the getter&setter names.


If we send a token string to the mixin template, we can avoid this problem:

    class Test
    {
        public:
            mixin properties!q{
                int foo;
            }
    }

And what happens inside the `properties` mixin is:

    mixin template properties(string declaration){
        private struct __Fields
        {
            mixin(declaration);
        }
        private __Fields __fields;
        // Create accessors properties for __fields's members.
        // Possibly create "m_*" aliases for __fields's members.
    }

Now the `foo` is not declared directly inside `Test` - instead it is declared inside `Test.__Fields` and accessed via the `__fields` struct. That means we are free to name our accessor property methods `foo` - since that identifier is still available in the main namespace of `Test`.

This solution has two problems(beside "x is evil" mantras):

1) Since the properties are created inside a struct, trying to probe `Test`(like `tupleof` or `std.traits.FieldTypeTuple`) will yield weird results. I believe I can solve this with a slightly more complex approach - instead of creating a member instance of `__Fields`, I can probe it myself and create `m_*` variations of the properties with the same types, storage classes etc.

2) If you use `typeof(this)` while declaring a property, it will yield `__Fields` instead of `Test`. I don't really know how to solve this, but this is an edge case - `typeof(this)` is usually used in mixins or templated methods, not in field declaration - so even with this problem, the mixin+string version is better suited for the common use-case.

Anyways, a small enhancement to dmd can help solve both problems easily - a mixin template that has an identifier and does not leak to the surrounding scope. But even without it - I can solve the first problem and the second problem concerns a rare use-case, so this is still a good solution if we want to declare the properties with the same names we are going to use them with...

Reply via email to