Custom attributes (again)

Ary Manzana ary at esperanto.org.ar
Fri Apr 6 02:50:05 PDT 2012


On 4/6/12 3:48 PM, Walter Bright wrote:
> On 4/6/2012 12:35 AM, Alex Rønne Petersen wrote:
>> It actually can be a problem. In .NET land, there are many attributes
>> across
>> many projects (and even in the framework itself) with the same names.
>> It turns
>> out that regular namespace lookup rules alleviate this problem.
>
>
> Perhaps a better scheme is:
>
> enum foo = 3;
>
> ...
>
> @attr(foo) int x;
>
> That way, foo will follow all the usual rules.

At least in .Net and Java it's something like this.

1. You declare your attributes. This is something good, because you have 
a place to say "This attribute is used for marking fields as 
non-serializable".

The syntax in Java for declaring an attribute:

public @interface Foo {
   String xxx;
   int yyy;
}

In D maybe @interface could be used to (in order to avoid introducing 
another keyword... or maybe use @attribute instead):

@attribute Foo {
   string xxx;
   int yyy;
}

2. You use them by using their names. What you are proposing if for 
attribute foo to be @attr(foo). But in Java it's @foo.

So in Java you would use that attribute like this:

@Foo(xxx = "hello", yyy = 1);
void func() {}

Then you can get the "Foo" attribute in func, and ask for it's xxx and yyy.

---

Now, your proposal is much simpler and it will become inconvenient in 
some cases. For example suppose you want to provide attributes for 
serialization (I guess the classic example). With your proposal it would be:

/// This is actually an attribute. Use this together with serialized_name.
enum serialize = 1;
enum serialized_name = 2;

@attr(serialize = true, serialized_name = "Foo")
int x;

Now, with the way things are done in Java and C#:

/// Marks a field to be serialized.
@attribute serialize {
   /// The name to be used.
   /// If not specified, the name of the member will be used instead.
   string name;
}

@serialize(name = "Foo")
int x;

You can see the syntax is much cleaner. The attribute declaration also 
serves as documentation and to group attributes related to the 
serialization process.

Now, to implement this is not very much difficult than what you proposed.

1. Introduce the syntax to define attributes. Piece of cake, since it's 
much more or less the syntax of a struct, but functions or nested types 
are not allowed. Parse them into an AttributeDecl or something like that.
2. When the compiler finds @attr(field = value) it uses normal lookup 
rules to find "attr". Then it checks it's an attributes. Then all fields 
are check in turn to see if their type match. You can probably put there 
anything that's compile-time evaluatable, though usually primitive types 
and strings are enough. If a field is not specified, it's type.init will 
be used.
3. The syntax for querying is almost the same as you proposed:

__traits(hasAttribute, x, serializable) // true
__traits(getAttribtue, x, serializable, name) // "Foo"

4. Declare the core attributes in object.di or similar: @safe, @nothrow, 
etc. You can also document them.
5. Probably deprecate __traits(isSafe) and so on, since hasAttribute can 
be used for that.


More information about the Digitalmars-d mailing list