1 /** Various suppressing hacks.
2  */
3 module nxt.suppressing;
4 
5 enum SuppressOptions
6 {
7 	destructor = 1,
8 	postblit = 2
9 }
10 
11 /** Suppress.
12  *
13  * See_Also: http://forum.dlang.org/post/dxakoknmzblxpgiibfmu@forum.dlang.org
14  */
15 struct Suppress(T, SuppressOptions options)
16 if (options != 0)
17 {
18 	private enum suppressPostblit   = (options & SuppressOptions.postblit)   != 0;
19 	private enum suppressDestructor = (options & SuppressOptions.destructor) != 0;
20 	private enum postblitName = __traits(hasMember, T, "__xpostblit") ? "__xpostblit" : "__postblit";
21 
22 	// Disguise T as a humble array.
23 	private ubyte[T.sizeof] _payload;
24 
25 	// Create from instance of T.
26 	this(T arg)
27 	{
28 		_payload = *cast(ubyte[T.sizeof]*)&arg;
29 	}
30 
31 	// Or forward constructor arguments to T's constructor.
32 	static if (__traits(hasMember, T, "__ctor"))
33 	{
34 		this(Args...)(Args args)
35 			if (__traits(compiles, (Args e){__traits(getMember, T.init, "__ctor")(e);}))
36 		{
37 			__traits(getMember, get, "__ctor")(args);
38 		}
39 	}
40 
41 	// Call dtor
42 	static if (!suppressDestructor)
43 	{
44 		~this() nothrow @nogc
45 		{
46 			destroy(get);
47 		}
48 	}
49 
50 	// Call postblit
51 	static if (!suppressPostblit)
52 	{
53 		static if (!__traits(isCopyable, T))
54 		{
55 			this(this) @disable;
56 		}
57 		else static if (__traits(hasMember, T, postblitName))
58 		{
59 			this(this)
60 			{
61 				__traits(getMember, get, postblitName)();
62 			}
63 		}
64 	}
65 
66 	// Pretend to be a T.
67 	@property
68 	ref T get()
69 	{
70 		return *cast(T*)_payload.ptr;
71 	}
72 
73 	alias get this;
74 }
75 
76 struct S1
77 {
78 	this(this) @disable;
79 	~this() nothrow @nogc
80 	{
81 		assert(0, "Don't touch my destructor!");
82 	}
83 }
84 
85 unittest {
86 	import std.exception;
87 	static assert(!__traits(compiles, (Suppress!S1 a) { auto b = a; }));
88 	static assert(__traits(compiles, (Suppress!(S1, SuppressOptions.postblit) a) { auto b = a; }));
89 
90 	/+ TODO: assertThrown({ Suppress!(S1, SuppressOptions.postblit) a; }()); +/
91 	assertNotThrown({ Suppress!(S1, SuppressOptions.postblit | SuppressOptions.destructor) a; }());
92 }