www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - RFC: patch statement

reply Dejan Lekic <dejan.lekic gmail.com> writes:
I know people her do not like to see proposals that change (add 
stuff to) the language. However, I strongly feel that for the 
testing purposes D should provide means to patch any object (no 
matter whether it is final or not!). Therefore I wonder what 
people think of adding a `patch(obj) {}` or perhaps change the 
semantics of the `with(obj) {}` so unittest writers can modify 
the object and set values.

The patch keyword would work ONLY inside unittest {} blocks AND 
inside functions annotated with  test annotation.

Imagine we have:

int myFun(Person person) { /* some logic here */ }

unittest {
   auto p = new Person() /* does not really matter which 
constructor we use */
   patch(p) {
     // here we can modify ANY attribute, no matter whether it is 
private or public
     p.fname = "Nikola"
     p.sname = "Tesla"
   }
   auto res = myFun(p)
   // do some assertions here
}

Similarly:

 test
void test_myFun() {
   // same code as in the unittest above.
}

I do not even know if patch() {} statement is even possible, that 
is the whole point of writing this, so people can enlighten me... 
:)

As I said in the introduction paragraph, for this purpose the 
semantics of the with statement could be changed, but I prefer a 
different keyword (patch) to be honest.
Apr 03 2017
next sibling parent Biotronic <simen.kjaras gmail.com> writes:
On Monday, 3 April 2017 at 11:16:57 UTC, Dejan Lekic wrote:
 I know people her do not like to see proposals that change (add 
 stuff to) the language. However, I strongly feel that for the 
 testing purposes D should provide means to patch any object (no 
 matter whether it is final or not!). Therefore I wonder what 
 people think of adding a `patch(obj) {}` or perhaps change the 
 semantics of the `with(obj) {}` so unittest writers can modify 
 the object and set values.

 The patch keyword would work ONLY inside unittest {} blocks AND 
 inside functions annotated with  test annotation.

 Imagine we have:

 int myFun(Person person) { /* some logic here */ }

 unittest {
   auto p = new Person() /* does not really matter which 
 constructor we use */
   patch(p) {
     // here we can modify ANY attribute, no matter whether it 
 is private or public
     p.fname = "Nikola"
     p.sname = "Tesla"
   }
   auto res = myFun(p)
   // do some assertions here
 }

 Similarly:

  test
 void test_myFun() {
   // same code as in the unittest above.
 }

 I do not even know if patch() {} statement is even possible, 
 that is the whole point of writing this, so people can 
 enlighten me... :)

 As I said in the introduction paragraph, for this purpose the 
 semantics of the with statement could be changed, but I prefer 
 a different keyword (patch) to be honest.
We can already do that. Proof of concept: // ======================= module bar; class A { private int n; public int GetN() { return n; } } // ======================= module foo; import bar; void main() { import std.stdio : writeln; auto a = new A(); assert(a.GetN() == 0); with (patch(a)) { // Look ma, magic! n = 3; } assert(a.GetN() == 3); } auto patch(T)(T value) { return Patch!T(value); } struct Patch(T) { T _payload; mixin PatchFields!T; } mixin template PatchFields(T, int __n = -1) { static if (__n == -1) { mixin PatchFields!(T, T.tupleof.length-1); } else { enum name = __traits(identifier, T.tupleof[__n]); mixin("auto "~name~"(U)(U value) { _payload.tupleof[__n] = value; }"); static if (__n > 0) { mixin PatchFields!(T, __n-1); } } }
Apr 03 2017
prev sibling parent Minty Fresh <minty fresh.com> writes:
On Monday, 3 April 2017 at 11:16:57 UTC, Dejan Lekic wrote:
 I know people her do not like to see proposals that change (add 
 stuff to) the language. However, I strongly feel that for the 
 testing purposes D should provide means to patch any object (no 
 matter whether it is final or not!). Therefore I wonder what 
 people think of adding a `patch(obj) {}` or perhaps change the 
 semantics of the `with(obj) {}` so unittest writers can modify 
 the object and set values.

 The patch keyword would work ONLY inside unittest {} blocks AND 
 inside functions annotated with  test annotation.

 Imagine we have:

 int myFun(Person person) { /* some logic here */ }

 unittest {
   auto p = new Person() /* does not really matter which 
 constructor we use */
   patch(p) {
     // here we can modify ANY attribute, no matter whether it 
 is private or public
     p.fname = "Nikola"
     p.sname = "Tesla"
   }
   auto res = myFun(p)
   // do some assertions here
 }

 Similarly:

  test
 void test_myFun() {
   // same code as in the unittest above.
 }

 I do not even know if patch() {} statement is even possible, 
 that is the whole point of writing this, so people can 
 enlighten me... :)

 As I said in the introduction paragraph, for this purpose the 
 semantics of the with statement could be changed, but I prefer 
 a different keyword (patch) to be honest.
It looks like what you're trying to do is set up object mocks for unit testing. In general, I find that well designed libraries provide such tools for testing, either in the form of factory functions or some other means of constructing mocks for test builds. I try to follow such patterns myself. Getting back to the immediate subject: You can already grant write access to whatever attributes with a bit of conditional compilation. Notably, defining accessors that exist only for unittest builds. You could even go so far as to define a generalized one, ie. version(unittest) { void patch(string attr, T)(Person p, T value) { __traits(getMember, p, attr) = value; } } And then, elsewhere: // Given p is some Person. p.patch!"fname" = "Nikola"; p.patch!"sname" = "Tesla"; So long as this is defined in the same module as the type, it'll be able to access protected and private fields.
Apr 03 2017