[cfe-dev] [RFC] Preliminary patch to support MSVC __declspec(property)

endlessroad1991 at gmail.com endlessroad1991 at gmail.com
Mon Dec 17 01:06:14 PST 2012


On Sat, Dec 15, 2012 at 2:50 AM, John McCall <rjmccall at apple.com> wrote:

> On Dec 13, 2012, at 10:01 PM, endlessroad1991 at gmail.com wrote:
>
> On Thu, Dec 13, 2012 at 4:23 PM, John McCall <rjmccall at apple.com> wrote:
>
>> On Dec 12, 2012, at 11:45 PM, endlessroad1991 at gmail.com wrote:
>>
>> Thanks, that's a very detailed and thorough comment.
>>
>> MSDN page for __declspec(property):
>> http://msdn.microsoft.com/en-us/library/yhfk0thd.aspx
>> we can declare "array-like" property:
>>         __declspec(property(get=GetX, put=PutX)) int x[][];
>> and access it with indices:
>>         var = obj->x[expr1][expr2];
>> it will be translated into:
>>         var = obj->GetX(expr1, expr2);
>>
>> And that's what "ParamCount" in "PropertyAttr" is for.
>> We don't know how many params until we parse to "int x[][]", so we have
>> to modify the Attr when we get here.
>>
>>
>> That documentation says you can declare this as "int x[]" and it permits
>> an arbitrary amount of subscripts, but apparently you can *also* add
>> multiple []s.  It would be good to understand the requirements and
>> limitations of this language feature as implemented in MSVC.  In particular:
>>
>> 1.  Can you directly template these declarations? (template <class T>
>> _declspec(property) T foo;)
>>
> No
>
>> 1a.  Are any of the following answers different if T is dependent, and if
>> so, are those likely to be bugs or actually intended?
>> 1b.  What if you have template <class T> struct A { _declspec(property) T
>> foo; }; and then instantiate the template at T=int[]?
>>
> Yes, this works fine.
>
>
> Does it add an expected index argument?
>   template <class T> struct A {
>     int *getFoo() { .. }
>     int getFoo(int index) { ... }
>     _declspec(property(get=getFoo)) T foo;
>   };
>
>   int test(A<int[]> &a) {
>     return a.foo[0];
>   }
>
> Does this call getFoo() or getFoo(int)?
>
Doesn't compile. Return type of getters must be exactly the same as
property type.

>
> 2.  What's the relationship between []s and the number of indices?
>> 2a.  Given _declspec(property) int x[], can you in fact use multiple
>> indices with it, like foo.x[2][3]?
>>
> No
>
>
> Okay, that actually explicitly contradicts MS's own documentation that you
> linked above.  Make sure you test this with an unscriptable element type —
> like int, not int*.
>
If getter with more/less parameters is defined, you can. Otherwise, you
can't.

>
> 2b.  If so, what happens if the "element type" is itself subscriptable?
>> 2b.i. _declspec(property) std::vector<int> x[] and foo.x[0][1]?
>>
> No
>
>
> What do you mean by "no" here?  Are you saying this program was rejected?
>  What's the error?
>
Doesn't compile. Error says "GetX() doesn't accept 2 arguments", just like
std::vector<int> is not subscriptable.

>
> 2b.ii. _declspec(property) int *x[] and foo.x[0][1]?
>>
> Yes, this works fine.
>
>> 2c.  Do parentheses change these answers at all?
>> 2c.i. _declspec(property) int* x[] and (foo.x[0])[1]?
>>
> Yes, this works fine.
>
>
> What about _declspec(property) int x[][] and (foo.x[0])[1]?
>
Compiles and runs as expected.

>
> 2c.ii. For that matter, given _declspec(property) int x[], is (foo.x)[0]
>> legal at all?
>>
> Yes, this works fine.
>
>
> Okay, good.
>
> 2d.  Given _declspec(property) int x[][], can you use fewer subscripts
>> than that?  Can you still use more?
>>
> Answer for both questions is NO.
>
>
> Good.
>
> 3.  Are there restrictions on the element type?
>>
> 3a.  Can it be a reference?
>>
> Yes
>
>
> Okay.  If I have _declspec(property(get=getFoo)) int &foo, can I do
>   x.foo = 7
> with the semantics of x.getFoo() = 7?
>
No. Seems that it's not that smart. All assignments will be translated into
setters.

>
> 3b.  Can it be a bounded array type?  (_declspec(property) int x[][10])
>>
> No. Seems that whatever you put in the brackets will be ignored, and
> treated as a param to getter/setter.
>
>
> Okay.
>
> 3c.  Can it be an unbounded array type if you use, e.g., a typedef?
>>  (typedef int intarr[]; _declspec(property) intarr x;, or intarr x[] for
>> that matter.)  Does this change the behavior of subscripting?
>>
> Surprisingly, in this situation, the [] in intarr is still interpreted as
> a param to getter/setter.
>
>
> Alright.  My question above about template arguments is designed to figure
> out whether this is a hack for non-dependent type sugar, or whether it
> works through anything that extends the type this way.
>
> 4.  What exactly does the element type do there?  Is it just for
>> type-checking the property against the accessors?
>> 4a.  I'd been assuming that the accessors were selected by overload
>> resolution at access time using the name given/inferred in the property
>> attribute.  Is this not true?  Are they selected/filtered by initial
>> type-checking somehow?
>>
> It is true. You can define "T GetV(int, int)" and "T GetV(double,
> double)". And getter will choose the best match like normal overload
> functions.
>
>
> Okay.
>
> 4b.  Is access control checked on the accessors from the declaration point
>> of the property or from the use point?
>> 4c.  Can the accessors be inherited from base classes?  Are normal
>> ambiguity controls enforced?  Can you scope-qualify the accessor names,
>> e.g. get=Base1::getElt?
>>
> Inherit: yes. Ambiguity: yes. Scope-quailify: No, seems that there can
> only be one identifier after get= or put=.
>
>> 4d.  Are the index types in the accessors limited in some way, or can
>> they be e.g. arbitrary class types?
>>
> They can be arbitary types.
>
>>
>> I'm sure we can find more questions. :)
>>
>> Considering this situation, is it still possible to do it in your
>> suggested way?
>>
>>
>> Sure, that's not really a problem.  One very important thing — something
>> I seem to have unconscionably forgotten put in my initial review — is that
>> this is way, way more than just a property on a FieldDecl;  this really
>> needs to be some new kind of Decl, probably named MSPropertyDecl.  That
>> node would then be a reasonable place to stash things like the expected
>> (minimum?) number of indices and any other important information from
>> type-checking the declaration, e.g. the possibly-filtered lookup results.
>>  You'll probably need to recognize the presence of the attribute in the
>> parser and enter into a completely different Sema entrypoint, kindof like
>> how the 'friend' keyword does.
>>
>> John.
>>
>
>
> Based on answers of these questions, let's guess how VC implements
> property.
> I think the design principle for them is, reuse existing compiler code as
> much as possible, and make the solution as simple as possible.
> Here are my opinions:
> 1. Any [], [10], or [] that comes from typedef, is treated as a param to
> getter/setter. Hence, they lose their origin semantics meaning.
>
>
> Right, you need to walk through type sugar.  If template instantiation
> can't add to the number of arguments, then you'll need to do this at
> template parse time and then remember it in the MSPropertyDecl.
>
> 2. For accessors, they will be translated into getter/setter.
>     i. property can be private, as long as getter/setter is public, they
> work well.
>
>
> Oh, now that's interesting.  Okay, you'll have to suppress access control
> on a member lookup that resolves to one of these.
>
>     ii. getter/setter will be chosen like any normal overloaded functions.
> 3. property are treated like normal members. So they can be inherited, and
> have ambiguity problem when a class has multiple base classes.
> The only problem here, is when the element type is subscriptable itself.
>     i. When it's pointer type, subscripting the property works as expected.
>     ii. When it's class type which has overwritten operator[] (no matter
> vector or self-defined class),  subscripting the property doesn't compile.
> I can't guess why this happens.
>
>
> If int x[] can, in fact, take more than one subscript (as documented),
> then I would guess that the rule is that all possible subscripts are
> consumed by the property *unless* the element type is "obviously
> subscriptable", which is probably defined as "a pointer type" and doesn't
> recognize classes with operator[]s.  Interesting corner cases here would be
>   int *&x[]; // does it realize that this result type is still "obviously
> subscriptable"?
>   int *x[]; // if I do a.x[0][1][2], does it steal both [0] and [1] for
> the subscript, or does it steal just [0] and then try to subscript int*
> twice (which will fail, of course)?
>
Here's two good samples, I think:

#include <iostream>
class C
{
public:
 __declspec(property(get=GetA, put=SetA)) int **A[];
 int** GetA(int i) { std::cout << "Get\n"; return &v; }
 void SetA(int i, int **value) { std::cout << "Set\n";}
 int *v;
};
int main()
{
 C c;
 c.A[123][0][0] = 2; // GetA()
 // c.A[123][0] = 2; // doesn't compile! "Cannot convert int to int*"
 c.A[1] = 0; // SetA()
}


#include <iostream>
class C
{
public:
 __declspec(property(get=GetA, put=SetA)) int *A[];
 int* GetA(int i) { std::cout << "Get 1\n"; return v; }
 int* GetA(int i, int j) { std::cout << "Get 2\n"; return v; }
 void SetA(int i, int *value) { std::cout << "Set 1\n"; }
 void SetA(int i, int j, int *value) { std::cout << "Set 2\n"; }
 int *v;
};
int main()
{
 C c;
 c.A[123][0] = 2; // Get 1
 c.A[1] = 0; // Set 1
}


>
> If int x[] really cannot take more than one subscript, then I have no idea
> why subscripting vector<int> x[] twice wouldn't compile;  probably just a
> compiler bug.
>
> John.
>

And, can you summarize how we should implement property, based on these
results?

-- 
Best Regards, Tong Shen (沈彤)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20121217/b34b50b2/attachment.html>


More information about the cfe-dev mailing list