www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - modifier, observer, and fences

reply Kevin Bealer <Kevin_member pathlink.com> writes:
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
parent reply kris <fu bar.org> writes:
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
parent Kevin Bealer <Kevin_member pathlink.com> writes:
In article <dsuq97$log$1 digitaldaemon.com>, kris says...
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?

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. Kevin
Feb 15 2006