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:
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.
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.