Very hacky solution to class private members

bauss jj_1337 at live.dk
Thu Jun 9 07:24:24 UTC 2022


Below is a way to actually make class private members using a 
modified version of the CTFE RNG demonstrated at d-idioms: 
https://p0nce.github.io/d-idioms/#Compile-time-RNG and some 
traits checking for calling function.

It's very hacky and I don't think it should be used seriously.

I was just wondering if it could be done like this and decided to 
do it for fun.

It only supports properties and functions.

First we need some boilerplate code:

```d
ulong timestampToUlong(string stamp)
{
     ulong result;

     foreach_reverse(c; stamp)
     {
         result += c;
         result *= 10;
     }

     return result;
}
enum counter(T,size_t x = [__traits(allMembers, 
mixin(T))].length)=x;
char[] member(ulong x)
{
     char[] buf = "void[0] _X0000000000000000;".dup;
     enum mapping = "0123456789abcdef";
     foreach_reverse(i; buf.length-17 .. buf.length-1)
     {
         buf[i] = mapping[x & 0xf];
         x >>= 4;
     }
     return buf;
}

mixin template next(T)
{
     mixin(member(counter!(T)));
}

template xorShift(T,size_t x = counter!(T))
{
     static if(x == 0)
     {
         enum xorShift = timestampToUlong(__TIMESTAMP__);
     }
     else
     {
         enum y = xorShift!(T,x-1);
         enum xorShift = 0x2545_f491_4f6c_dd1dUL
             * (((y ^ (y >> 12)) ^ ((y ^ (y >> 12)) << 25))
             ^ (((y ^ (y >> 12)) ^ ((y ^ (y >> 12)) << 25)) >> 
27));
     }
}

mixin template hiddenProp(T, string name, Within, string ts = 
to!string(xorShift!(Within)))
{
     mixin(T.stringof ~ " _hidden_" ~ ts ~ "_" ~ name ~ ";");
     mixin("private @property " ~ T.stringof ~ " " ~ name ~ 
"(string caller=__FUNCTION__)() if (caller.startsWith(`" ~ 
fullyQualifiedName!(Within) ~ "`)) {return _hidden_" ~ ts ~ "_" ~ 
name ~ ";}");
     mixin("private @property void " ~ name ~ "(string 
caller=__FUNCTION__)(" ~ T.stringof ~ " value) if 
(caller.startsWith(`" ~ fullyQualifiedName!(Within) ~ "`)) { 
_hidden_" ~ ts ~ "_" ~ name ~ " = value;}");

     mixin next!(Within);
}

mixin template hiddenFn(string name, string fnBody, string[] 
params = [], T = void, Within, string ts = 
to!string(xorShift!(Within)))
{
     mixin(T.stringof ~ " " ~ name ~ "(string 
caller=__FUNCTION__)(" ~ params.join(",") ~ ") if 
(caller.startsWith(`" ~ fullyQualifiedName!(Within) ~ "`)) { " ~ 
fnBody ~ " }");

     mixin next!(Within);
}
```

And then we have the actual code for the class etc.

```d
public class Foo
{
     mixin hiddenProp!(int, "x", typeof(this));
     mixin hiddenProp!(int, "y", typeof(this));

     mixin hiddenFn!("calc", q{
         return x * y;
     }, ["int x", "int y"], int, typeof(this));

     void test()
     {
         x = calc(20, 5); // ok
         writeln(x); // ok
     }
}

void main()
{
     auto foo = new Foo;
     foo.test(); // ok - prints 100
     //foo.x = 300; // not ok (compile time error.)
     //writeln(foo.x); // not ok (compile time error.)
     //int z = foo.calc(); // not ok (compile time error.)
     //writeln(z);
}
```

It's unbelievably stupid, but it works.

In theory you can cheat it by changing the caller to whatever you 
desire or you can attempt to guess the backing variables for the 
properties (although that's almost impossible.) - neither is 
worth the trouble.

You can't however accidentally call this from outside the class.

Anyway just wanted to have some fun, but please don't use this 
garbage in all seriousness.


More information about the Digitalmars-d mailing list