RFC: patch statement

Biotronic via Digitalmars-d digitalmars-d at puremagic.com
Mon Apr 3 05:00:13 PDT 2017


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);
         }
     }
}


More information about the Digitalmars-d mailing list