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

Reply via email to