digitalmars.D - modifier, observer, and fences
- Kevin Bealer (123/126) Feb 15 2006 A potential solution to the const issue occured to me. I think const is...
- kris (5/16) Feb 15 2006 Interesting idea. What happens when foo() calls bar(char[]) with x as a
- Kevin Bealer (4/20) Feb 15 2006 I think the error would happen at compile time. Passing an inref object...
A potential solution to the const issue occured to me. I think const is like "private" and "protected". The language doesn't put fences around things, but it provides tools so that you can build your own fences. The issue (as a user) with C++ is that anyone who makes something const forces everyone else into the same boat up and down the call stack. So my idea goes like this - allow people to pass "in by reference", and introduce two keywords, "modifier" and "observer" which determine whether a particular field or member is available to someone with "inref" access to an object. However, in most cases they would not be needed. Modifier methods are accessible to users of an object, unless the object is an "in reference" object. Observer members are always available. Now, most methods people write fit one of several simple profiles that allow the compiler to deduce correctly whether it is a modifier or observer. Here are my rules: 1. The user can specify any method as modifier or observer, overriding the default if one exists. 2. Almost all of the "standard" methods default to modifier or observer in the obvious way: opEquals(), opCmp(), etc are "observer" and opIndexAssign, opAddAssign, etc are "modifier" methods. 5. IFC (implicit function call) type methods have the obvious default: "int measure()" defaults to 'observer', but "void measure(int)" defaults to 'modifier'. After all methods that return void are *usually* modifiers - else why are you calling it? Methods that return a value but take no arguments are usually observers (int stack::pop() is an exception to this rule...). 6. Editing fields with foo.x = 5, can easily be labeled as read or write in most cases. This shouldn't be too hard. Hard cases default to allowed. 7. If a method or action is not marked and cannot be deduced, it has to be considered an observer, so that existing code works, and so that people who don't care can basically ignore the feature. Sorry for those who love the policework involved in const, but I think this is a reasonable compromise. 8. Other actions, like taking a pointer to a field, opApply(), and so on are also observers (meaning, you can use them on const objects). Again, this is the correct default for practical reasons. You can override it if you need a fence for whatever reason. 9. The compiler will at no time examine method bodies to deduce whether methods are observer or modifier.Either: the signature fits a simple pattern (IFC-like read/write methods); Or, its a known kind of method (i.e. an opCmp() vs opIndexAssign); Else no protections are enforced.Example class code, O means observer, M means modifier: : struct foo { : int a; // ifc actions here will be policed as usual : int b observer; // ifc actions are ignored for const purposes here : : // These five are compiler deduceable: : : void set_a(int q) { a = q; } // M : int read_b() { return b; } // O : : void opAddAssign(in foo other) // M : { ... } : : void opIndex() { ... } // O : : opEqual() { ... } // O : : : // All these are not deduceable, and will : // default to "observer", i.e. no protections. : : int changes(int q) // ? : { ... } : : void mystery() // ? : { ... } : : : // You can override the assumptions. : : // This pop_stack() would normally be considered "observer", since : // it looks like an IFC "read" method, but I can override that. : : int pop_stack() modifier : { ... } : : : // This would normally be considered a "modifier" because it : // returns void and takes arguments, but I override it too. : : void verify_value(int expected_a) observer : { : assert(a == expected_a); : } : }; Example client code: : int bar(inref foo myfoo) : { : int x = myfoo.a; // this is considered okay : myfoo.b = 37; // error, obvious modify : : // compiler punts on this, i.e. doesn't enforce protections. : // it doesn't fit a simple rule, so we can't tell easily, so : // it is considered legal: : : int * x = & myfoo.a; : some_function(x); : : // this is legal (opEqual is an observer) : myfoo second; : : if (second == a) { : // do something : } : : // so are these; they don't fit simple deductive rules, : // so they're assumed to be okay here. : : a.mystery(); : int b2 = a.changes(99); : } I realize there are many cases that this mechanism cannot enforce, but the goal (I think) it to provide a useful design tool. A pointer can always get around these fields, but it allows a moderate amount of fence building for the const member "trouble spots". Since opIndexAssign() is considered a "modifier" call, this idea can also cover the most famous case: :void foo(in char[] x) :{ : x[0] = 'a'; // illegal : : char[] y = x; : : y[0] = 'b'; // this is not detected, but I maintain that that's okay. :} Kevin
Feb 15 2006
Kevin Bealer wrote: [snip]Since opIndexAssign() is considered a "modifier" call, this idea can also cover the most famous case: :void foo(in char[] x) :{ : x[0] = 'a'; // illegal : : char[] y = x; : : y[0] = 'b'; // this is not detected, but I maintain that that's okay. :}Interesting idea. What happens when foo() calls bar(char[]) with x as a (non 'in') argument? The compiler should throw an error at the call-site, but how would it do that?
Feb 15 2006
In article <dsuq97$log$1 digitaldaemon.com>, kris says...Kevin Bealer wrote: [snip]I think the error would happen at compile time. Passing an inref object to an out or inout parameter is clearly a "modifier" type operation. KevinSince opIndexAssign() is considered a "modifier" call, this idea can also cover the most famous case: :void foo(in char[] x) :{ : x[0] = 'a'; // illegal : : char[] y = x; : : y[0] = 'b'; // this is not detected, but I maintain that that's okay. :}Interesting idea. What happens when foo() calls bar(char[]) with x as a (non 'in') argument? The compiler should throw an error at the call-site, but how would it do that?
Feb 15 2006