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

Hey guys, I’ve worked out a preliminary patch to support MSVC __declspec(property), based on 3.2RC2.

Patch and a simple test cpp file attached. Please compile the test case with -fms-extensions.

Current status:

  • Support getter/setter- Support template (see attached test file)
  • No support for +=, -=, etc
  • No support for implicit type conversion for arguments of getter/setter functions

Brief introduction on implementation:

  1. Let Parser parse property declaration.
  2. Add a “PropertyAttr” class to store getter/putter function names.
  3. Notice that there can be “” after property declaration, and number of “” indicates number of args for getter/setter function(see attached test file). Add a field to QualType to store number of “”. I know it’s horrible, but for a working prototype, that’s enough.
  4. Add a “MSPropertyExpr”. When we encounter property usage in Parser, we don’t know whether it’s get or set. So we have to add an action AFTER Sema, or do it in CodeGen. Either way, while parsing, all we know is arguments for getter/setter.
  5. In CodeGen, deal with “MSPropertyExpr”. If it’s “scalar” in CodeGen, it’s a get, replace it with a CallExpr to getter. If it’s “LValue” on the left side of an assignment, replace the whole assignment with a CallExpr to setter.

I know the timing is really bad…
But, any comments?

1.patch (24.9 KB)

test.cpp (309 Bytes)

Clang has a policy of only supporting compatibility features that are
widely used. Is there a large codebase that depends on this, and has
this been discussed?

As for the patch itself, the test should be a part of it, I can't
comment the implementation. Have a look at the existing test, it
should have something like this at he top

// RUN: %clang_cc1 -fsyntax-only -verify %s -ms-compatibility
// expected-no-diagnostics

Could you submit this via Phab (http://llvm-reviews.chandlerc.com/)?

I have some comments about the code, and Phab makes it much easier to point to the relevant code :slight_smile:

This feature is used in MFC, which may be a big reason why Clang cannot handle those headers, and possibly other windows headers as well. I think I remember MFC issues coming up from time to time on this mailing list.

Also, this feature would be helpful to implement C++ AMP with better compatibility, which is a project I'm working on (presented at the recent LLVM Developer's Meeting if you want more information).

Regards,
Dillon

Handling this in IR-generation is not a great idea; we did this for Objective-C properties for a very long time and eventually got fed up with it. You should look into pseudo-object expressions; I actually had this language extension in the back of my mind when I was designing them.

John.

Thanks for the advice.

Dillon has explained property is a widely used feature.

This patch is far from complete, and I’ve brought it up here to discuss the best way to implement the property feature. After we get a solution, I will make sure the patch conform to LLVM & Clang coding standards :slight_smile:

Yes, if we put it in CodeGen, we still have to do much work which is Sema’s responsibility, and that’s ugly.

I looked into pseudo objects and ObjC property implementation.I see that set is handled in Sema::BuildBinOp(which calls Sema::checkPseudoObjectAssignment), and get is handled in Sema::CheckPlaceHolder, which is called in many places. And we are sure that L-Value property(thus set) won’t be rewritten to getter call, because Sema::CheckPlaceHolder will only handle R-Value situations.

Are these “deductions” correct? If so, I will reimplement property using pseudo objects.
Thanks for the info!

Well, you would actually handle these in the functions that are *called* by those places, which are all in SemaPseudoObject.cpp. But that's the basic idea.

John.

Hi all, I’ve rewritten the __declspec(property) implementation,using PseudoObject.

If you’re comfortable with this patch, I’ll work on tests, code-style, and post it to cfe-commits.

v2.patch (22.9 KB)

Posting to cfe-commits is appropriate for detailed review anyway. I've left cfe-dev CC'ed, but please remove it as a CC if you reply.

+class MSPropertyRefExpr : public Expr {
+private:
+ MemberExpr *MemberExpr;
+ ArrayRef<Stmt*> IndexExprs;
+ SourceLocation StartLoc, EndLoc;
+ ASTContext& AstContext;
+ Expr *GetterExpr, *SetterExpr;

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.
Considering this situation, is it still possible to do it in your suggested way?

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 _declspec(property) T foo;)
    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 struct A { _declspec(property) T foo; }; and then instantiate the template at T=int[]?
  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]?
    2b. If so, what happens if the “element type” is itself subscriptable?
    2b.i. _declspec(property) std::vector x[] and foo.x[0][1]?
    2b.ii. _declspec(property) int x[] and foo.x[0][1]?
    2c. Do parentheses change these answers at all?
    2c.i. _declspec(property) int
    x[] and (foo.x[0])[1]?
    2c.ii. For that matter, given _declspec(property) int x[], is (foo.x)[0] legal at all?
    2d. Given _declspec(property) int x[][], can you use fewer subscripts than that? Can you still use more?
    2e. Just to verify, what happens if you subscript _declspec(property) int x;? (no brackets at all)
  3. Are there restrictions on the element type?
    3a. Can it be a reference?
    3b. Can it be a bounded array type? (_declspec(property) int x[][10])
    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?
    3d. Do parens in the declarator affect any of this?
  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?
    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?
    4d. Are the index types in the accessors limited in some way, or can they be e.g. arbitrary class types?

I’m sure we can find more questions. :slight_smile:

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.

One comment: A field part of a __declspec(property) can be incomplete.
For example:

class A {
    A get_A() const;
    __declspec(property(get=get_A)) A default_A;
};

Your patch will emit an error for that case, it must be accepted.

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 _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 struct A { _declspec(property) T foo; }; and then instantiate the template at T=int[]?

Yes, this works fine.

  1. 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

2b. If so, what happens if the “element type” is itself subscriptable?
2b.i. _declspec(property) std::vector x[] and foo.x[0][1]?

No

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.

2c.ii. For that matter, given _declspec(property) int x[], is (foo.x)[0] legal at all?

Yes, this works fine.

2d. Given _declspec(property) int x[][], can you use fewer subscripts than that? Can you still use more?

Answer for both questions is NO.

2e. Just to verify, what happens if you subscript _declspec(property) int x;? (no brackets at all)

No

  1. Are there restrictions on the element type?
    3a. Can it be a reference?

Yes

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.

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.

3d. Do parens in the declarator affect any of this?

No

  1. 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.

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.

Oh thanks, you’re right.

So when we check type for property, we treat the type as if it’s a return type of a member function, and not a type of a member var.

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 _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 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 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)?

  1. 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*.

2b. If so, what happens if the “element type” is itself subscriptable?
2b.i. _declspec(property) std::vector 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?

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]?

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.

  1. 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?

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.

  1. 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. :slight_smile:

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.

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.

Oh, now that’s interesting. Okay, you’ll have to suppress access control on a member lookup that resolves to one of these.

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)?

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

John.

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. :slight_smile:

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?

Here’s a try to summarize implementation detail about __decpspec(property):

  1. Add a MSPropertyDecl, because:
  • Property can be private, as long as getter/setter is public. This is different from regular members.
  • Type of property can be imcomplete, like return type of a member function. This is different from regular members.
  • We can use it to store how many parameters should be used for getter/setter
  1. Add a MSPropertyRefExpr, like ObjCPropertyRefExpr. Preserve all (index) parameters for getter/setter calls in ast. This is necessary because (a.v)[1][2] and (a.v[1])[2] are both legal.
  2. Lookup actual getter/setter, and generate CallExpr in PseudoObject related routines.

And a complete test, covering all that I can think of now.

#include
#include
class C
{
// property can be private, as long as getter/setter is public.
__declspec(property(get=GetA, put=SetA)) int A[123][321];
public:
// We choose getter/setter like regular overloaded functions.
int GetA() { std::cout << “Get 0\n”; return 0; }
int GetA(double i) { std::cout << “Get 1\n”; return 0; }
int GetA(int i) { std::cout << “Get 1i\n”; return 0; }
int GetA(double i, double j) { std::cout << “Get 2\n”; return 0; }
void SetA(int value) { std::cout << “Set 0\n”; }
void SetA(double i, double value) { std::cout << “Set 1\n”; }
void SetA(double i, double j, int value) { std::cout << “Set 2\n”; }
// Property type can be reference.
__declspec(property(get=GetA2, put=SetA2)) int& A2;
int& GetA2() { std::cout << “GetA2\n”; return ri; }
void SetA2(int& v) { std::cout << “SetA2\n”; ri = v; }
// “[]” can be filled with a integer number, which is legal, but useless.
// And “[]” that appear in typedef will also be treated as placeholder for parameter.
typedef int int_arr[10][10];
__declspec(property(get=GetA3, put=SetA3)) int_arr A3;
int GetA3() { std::cout << “GetA3 0\n”; return 0; }
int GetA3(double i) { std::cout << “GetA3 1\n”; return 0; }
int GetA3(double i, double j) { std::cout << “GetA3 2\n”; return 0; } // We can only leave this getter.
// If property type is pointer, only getter/setter with NO parameters will be picked.
__declspec(property(get=GetA4, put=SetA4)) int A4;
int
GetA4() { std::cout << “GetA4 0\n”; return ppp; }
int*** GetA4(int i) { std::cout << “GetA4 1\n”; return ppp; }
int*** GetA4(int i, int j) { std::cout << “GetA4 2\n”; return ppp; }
void SetA4(int ***v) { std::cout << “SetA4 0\n”; }
void SetA4(int i, int ***v) { std::cout << “SetA4 1\n”; }
void SetA4(int i, int j, int ***v) { std::cout << “SetA4 2\n”; }
// Even if property type can be subscripted, we cannot directly subsrcipt it by appending “[]”.
declspec(property(get=GetA5)) std::vector A5[];
std::vector GetA5(int i) { std::cout << “GetA5 1\n”; return v; }
std::vector GetA5(int i, int j) { std::cout << “GetA5 2\n”; return v; }
std::vector v;
int i;
int &ri;
int *p;
int **pp;
int ***ppp;
C(int &ri
): ri(ri
) { p = &i, pp = &p, ppp = &pp; }
};
int main()
{
int ri = 5445;
C c(ri);
int i = c.A; // Get 0
int j = c.A[1.0f]; // Get 1
int j2 = c.A[1]; // Get 1i
int k = c.A[1.0f][2.0f]; // Get 2
int k1 = (c.A)[1.0f][2.0f]; // Get 2
int k2 = (c.A[1.0f])[2.0f]; // Get 2
int& ir = c.A2; // GetA2
std::cout << ir << “\n”; // 5445
int ai = 4;
c.A2 = ai; // SetA2 // SetA2 must exist!
std::cout << c.A2 << “\n”; // GetA2 // 4
ai = 3;
std::cout << c.A2 << “\n”; // GetA2 // 4
int ii = c.A3; // GetA3 0
int jj = c.A3[1.0]; // GetA3 1
int kk = c.A3[1.0][2.0]; // GetA3 2
int ll = c.A3[1]; // GetA3 1
int ***a1 = c.A4; // GetA4 0
int **b1 = c.A4[0]; // GetA4 0
int *c1 = c.A4[0][0]; // GetA4 0
int d1 = c.A4[0][0][0]; // GetA4 0
// int d2 = c.A4[0][0][0][0]; // Doesn’t compile
c.A4 = 0; // SetA4 0
c.A4[0] = c.pp; // GetA4 0
c.A4[0][0] = c.p; // GetA4 0
c.A4[0][0][0] = 0; // GetA4 0
std::vector v = c.A5[0]; // GetA5 1
v = c.A5[0][0]; // GetA5 2
//int v2 = c.A5[0][0]; // Doesn’t compile, “Cannot convert vector to int”
//int v3 = c.A5[0][0][0]; // Doesn’t compile, “Cannot convert vector to int”
}

Or, we can only implement a WELL-DEFINED subset of property: no [] support.

  • I looked at ATL headers, those properties are all defined without [].
  • My work is financially supported by GEMS8(http://wwwm.gems8.com), which only use property without [].
  • Any property usage involving [] seems totally “undefined”. I guess there are only god-know-what rules defined by VC developers, and they’are not intended for real-world use.
    Is this suggestion ok?

Does it not compile at template-parse time, at instantiation time, or at time of first use? In other words, does it not compile if you just have the template definition but don’t use it? What about if you have the template definition, and you instantiate it at some type, but you don’t use the property declaration?

Interesting.

Okay.

Well, that does make it simple.

Well, that’s true, you can’t. But if the RHS were (int*) 2, this would presumably compile (calling GetA()).

Very nice test case. Okay, one more: