classes by value

Jonathan Marler johnnymarler at gmail.com
Thu Dec 14 14:31:33 UTC 2017


Thought I would share this little nugget.  It's a simple module 
to enable "classes by value".  A good demonstration of the power 
of "alias this". I had originally implemented this using 
"opDispatch" and only after I was done did I realize I could have 
made my life much simpler if I had gone with "alias this" in the 
first place :)

https://run.dlang.io/gist/marler8997/799286523e139c65c6de1b37b6729a72?compiler=dmd&args=-unittest%20-main

/**
Value is a template that represents a class object value.  This 
is in contrast to a
normal class object which is a pointer to a class object value.

---
void foo(Value!SomeClass value)
{
     // ... use value
}
---
*/
struct Value(T) if (is(T == class))
{
     @disable this();
     private void[__traits(classInstanceSize, T)] ____classdata = 
void;
     @property pragma(inline) T ____classptr()
     {
         return cast(T)&this;
     }

     alias ____classptr this;
}
/**
Initializes a class value
*/
void initClassValue(T, Args...)(Value!T* classValue, Args args) 
if (is(T == class))
{
     import std.conv : emplace;

     emplace(classValue.____classptr, args);
}
/**
Use to create a class object value.
---
auto classValue = createClasValue!SomeClass;
---
*/
Value!T createClassValue(T, Args...)(Args args) if (is(T == 
class))
{
     import std.conv : emplace;

     Value!T value = void;
     emplace(value.____classptr, args);
     return value;
}

/**
Creates a Value!T class from class T.
---
Foo foo; // foo is a class

Value!Foo fooValue1 = void;
copyClassValue(&fooValue1, classObject);

auto foo2 = foo.copyClassValue();
---
*/
void copyClassValue(T)(Value!T* classValue, T classObject) if 
(is(T == class))
{
     classValue.____classdata[0 .. __traits(classInstanceSize, T)] 
=
         (cast(ubyte*) classObject)[0 .. 
__traits(classInstanceSize, T)];
}
/// ditto
Value!T copyClassValue(T)(T classObject) if (is(T == class))
{
     Value!T value = void;
     copyClassValue(&value, classObject);
     return value;
}

unittest
{
     static class Foo
     {
         int x;
         this(int x)
         {
             this.x = x;
         }

         void assertValue(int expected)
         {
             assert(x == expected);
         }

         void doNothing()
         {
         }
     }

     static void testValueClassArg(Value!Foo foo, int 
valueToAssert)
     {
         foo.assertValue(valueToAssert);
     }

     {
         auto foo = createClassValue!Foo(946);
         assert(foo.x == 946);
         foo.assertValue(946);
         testValueClassArg(foo, 946);

         initClassValue(&foo, 391);
         assert(foo.x == 391);
         foo.assertValue(391);
         testValueClassArg(foo, 391);
     }
     {
         auto foo = new Foo(1234);
         assert(foo.x == 1234);
         foo.assertValue(1234);

         Value!Foo fooValue = void;
         copyClassValue(&fooValue, foo);
         assert(fooValue.x == 1234);
         fooValue.assertValue(1234);
         testValueClassArg(fooValue, 1234);

         auto fooValue2 = foo.copyClassValue();
         assert(fooValue2.x == 1234);
         fooValue2.assertValue(1234);
         testValueClassArg(fooValue2, 1234);

         testValueClassArg(foo.copyClassValue(), 1234);
     }
}


More information about the Digitalmars-d mailing list