www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - classes by value

reply Jonathan Marler <johnnymarler gmail.com> writes:
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);
     }
}
Dec 14 2017
parent reply Dgame <r.schuett.1987 gmail.com> writes:
On Thursday, 14 December 2017 at 14:31:33 UTC, Jonathan Marler 
wrote:
 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);
     }
 }
Strongly reminds me of scoped
Dec 14 2017
parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 14 December 2017 at 14:45:51 UTC, Dgame wrote:
 Strongly reminds me of scoped
Declaring a variable as `scoped` prevents that variable from escaping the scope it is declared in. This restriction allows the compiler certain optimizations, such as allocating classes on the stack, i.e. ``` scoped foo = new Foo(); ``` this optimization is well-known to D programmers so class allocation on the stack is strongly associated with the "scoped" modifier which is probably why this "class by value" snippet reminds you of "scoped". Even though "classes by value" can be implied in certain usages of "scoped", "scoped" carries with it extra semantics that "classes by value" on it's own does not. This snippet allows "classes by value" on it's own, which enables different ways of using classes (for example, creating an array of classes by value `Value!MyClass[]`).
Dec 14 2017
parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Thursday, 14 December 2017 at 16:10:17 UTC, Jonathan Marler 
wrote:
 On Thursday, 14 December 2017 at 14:45:51 UTC, Dgame wrote:
 Strongly reminds me of scoped
Declaring a variable as `scoped` prevents that variable from escaping the scope it is declared in. This restriction allows the compiler certain optimizations, such as allocating classes on the stack, i.e. ``` scoped foo = new Foo(); ``` this optimization is well-known to D programmers so class allocation on the stack is strongly associated with the "scoped" modifier which is probably why this "class by value" snippet reminds you of "scoped". Even though "classes by value" can be implied in certain usages of "scoped", "scoped" carries with it extra semantics that "classes by value" on it's own does not. This snippet allows "classes by value" on it's own, which enables different ways of using classes (for example, creating an array of classes by value `Value!MyClass[]`).
I think what Dgame meant was: https://dlang.org/phobos-prerelease/std_typecons#scoped. For the built-in scoped classes, the keyword is 'scope', not 'scoped': https://dlang.org/spec/class.html#auto. So you can have both: ``` scope foo = new Foo(); ``` and: ``` auto foo = scoped!Foo(); ``` and finally: ``` scope foo = scoped!Foo(); ```
Dec 14 2017
next sibling parent Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Thursday, 14 December 2017 at 16:40:33 UTC, Petar Kirov 
[ZombineDev] wrote:
 On Thursday, 14 December 2017 at 16:10:17 UTC, Jonathan Marler 
 wrote:
 On Thursday, 14 December 2017 at 14:45:51 UTC, Dgame wrote:
 Strongly reminds me of scoped
Declaring a variable as `scoped` prevents that variable from escaping the scope it is declared in. This restriction allows the compiler certain optimizations, such as allocating classes on the stack, i.e. ``` scoped foo = new Foo(); ``` this optimization is well-known to D programmers so class allocation on the stack is strongly associated with the "scoped" modifier which is probably why this "class by value" snippet reminds you of "scoped". Even though "classes by value" can be implied in certain usages of "scoped", "scoped" carries with it extra semantics that "classes by value" on it's own does not. This snippet allows "classes by value" on it's own, which enables different ways of using classes (for example, creating an array of classes by value `Value!MyClass[]`).
I think what Dgame meant was: https://dlang.org/phobos-prerelease/std_typecons#scoped. For the built-in scoped classes, the keyword is 'scope', not 'scoped': https://dlang.org/spec/class.html#auto. So you can have both: ``` scope foo = new Foo(); ``` and: ``` auto foo = scoped!Foo(); ``` and finally: ``` scope foo = scoped!Foo(); ```
Actually, in the first example foo is both stack-allocated and has the scope storage class. Perhaps what I meant can be expressed as: ``` scope Foo foo; with (auto tmp = new Foo()) foo = tmp; ```
Dec 14 2017
prev sibling next sibling parent Jonathan Marler <johnnymarler gmail.com> writes:
On Thursday, 14 December 2017 at 16:40:33 UTC, Petar Kirov 
[ZombineDev] wrote:
 I think what Dgame meant was: 
 https://dlang.org/phobos-prerelease/std_typecons#scoped. For 
 the built-in scoped classes, the keyword is 'scope', not 
 'scoped': https://dlang.org/spec/class.html#auto.

 So you can have both:

 ```
 scope foo = new Foo();
 ```

 and:
 ```
 auto foo = scoped!Foo();
 ```

 and finally:
 ```
 scope foo = scoped!Foo();
 ```
I completely forgot about the "scoped" template. Yes it appears to be almost exactly the same, the implementation is very similar to the code I came up with (with some of the robust "details" filled in).
Dec 14 2017
prev sibling next sibling parent reply Seb <seb wilzba.ch> writes:
On Thursday, 14 December 2017 at 16:40:33 UTC, Petar Kirov 
[ZombineDev] wrote:
 ```
 scope foo = new Foo();
 ```
That's about to be deprecated in favor of scoped!Foo: https://dlang.org/deprecate.html#scope%20for%20allocating%20classes%20on%20the%20stack
Dec 15 2017
parent reply Dgame <r.schuett.1987 gmail.com> writes:
On Friday, 15 December 2017 at 09:18:23 UTC, Seb wrote:
 On Thursday, 14 December 2017 at 16:40:33 UTC, Petar Kirov 
 [ZombineDev] wrote:
 ```
 scope foo = new Foo();
 ```
That's about to be deprecated in favor of scoped!Foo: https://dlang.org/deprecate.html#scope%20for%20allocating%20classes%20on%20the%20stack
Since scope was revived with DIP-1000 we will see about that. I doubt that the deprecation will stay.
Dec 15 2017
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Friday, December 15, 2017 11:10:42 Dgame via Digitalmars-d wrote:
 On Friday, 15 December 2017 at 09:18:23 UTC, Seb wrote:
 On Thursday, 14 December 2017 at 16:40:33 UTC, Petar Kirov

 [ZombineDev] wrote:
 ```
 scope foo = new Foo();
 ```
That's about to be deprecated in favor of scoped!Foo: https://dlang.org/deprecate.html#scope%20for%20allocating%20classes%20on %20the%20stack
Since scope was revived with DIP-1000 we will see about that. I doubt that the deprecation will stay.
DIP 1000 is half killing it. DIP 1000 is using scope for something very different, and it has nothing to do with putting classes on the stack. However, as an optimization, it may put a class on the stack if it determines that the the class object is unique. Anyone who wants to guarantee it should be using std.typecons.scoped. - Jonathan M Davis
Dec 15 2017
next sibling parent Dgame <r.schuett.1987 gmail.com> writes:
On Friday, 15 December 2017 at 12:56:41 UTC, Jonathan M Davis 
wrote:
 On Friday, December 15, 2017 11:10:42 Dgame via Digitalmars-d 
 wrote:
 On Friday, 15 December 2017 at 09:18:23 UTC, Seb wrote:
 [...]
Since scope was revived with DIP-1000 we will see about that. I doubt that the deprecation will stay.
DIP 1000 is half killing it. DIP 1000 is using scope for something very different, and it has nothing to do with putting classes on the stack. However, as an optimization, it may put a class on the stack if it determines that the the class object is unique. Anyone who wants to guarantee it should be using std.typecons.scoped. - Jonathan M Davis
Since scope is used inside dmd I'm sure it will stay the way it is or the way you've described, yes.
Dec 15 2017
prev sibling parent Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Friday, 15 December 2017 at 12:56:41 UTC, Jonathan M Davis 
wrote:
 On Friday, December 15, 2017 11:10:42 Dgame via Digitalmars-d 
 wrote:
 On Friday, 15 December 2017 at 09:18:23 UTC, Seb wrote:
 On Thursday, 14 December 2017 at 16:40:33 UTC, Petar Kirov

 [ZombineDev] wrote:
 ```
 scope foo = new Foo();
 ```
That's about to be deprecated in favor of scoped!Foo: https://dlang.org/deprecate.html#scope%20for%20allocating%20classes%20on %20the%20stack
Since scope was revived with DIP-1000 we will see about that. I doubt that the deprecation will stay.
DIP 1000 is half killing it. DIP 1000 is using scope for something very different, and it has nothing to do with putting classes on the stack. However, as an optimization, it may put a class on the stack if it determines that the the class object is unique. Anyone who wants to guarantee it should be using std.typecons.scoped. - Jonathan M Davis
No, DIP1000 only enhances the existing meaning of `scope` classes. It is not an optimization that may or may not happen. When you do `scope foo = new Foo()` and `Foo` is a class you always get stack allocation, regardless of whether you're compiling with `-dip1000` or not. It has been defined in the spec and implemented for many years now [1]. Many project rely heavily on that (e.g. [2][3]) and it will not be deprecated any time soon, regardless of whatever https://dlang.org/deprecate says. [1]: https://github.com/dlang/dmd/commits/master/test/runnable/testscope.d [2]: https://github.com/sociomantic-tsunami/ocean/search?utf8=%E2%9C%93&q=scope&type= [3]: https://github.com/search?utf8=%E2%9C%93&q=scope+repo%3Adlang%2Fdmd+extension%3Ad+path%3Asrc%2Fdmd&type=Code ref=advsearch&l=&l= (look for "scope " in scr/)
Dec 15 2017
prev sibling parent Dgame <r.schuett.1987 gmail.com> writes:
On Thursday, 14 December 2017 at 16:40:33 UTC, Petar Kirov 
[ZombineDev] wrote:
 On Thursday, 14 December 2017 at 16:10:17 UTC, Jonathan Marler 
 wrote:
 On Thursday, 14 December 2017 at 14:45:51 UTC, Dgame wrote:
 Strongly reminds me of scoped
Declaring a variable as `scoped` prevents that variable from escaping the scope it is declared in. This restriction allows the compiler certain optimizations, such as allocating classes on the stack, i.e. ``` scoped foo = new Foo(); ``` this optimization is well-known to D programmers so class allocation on the stack is strongly associated with the "scoped" modifier which is probably why this "class by value" snippet reminds you of "scoped". Even though "classes by value" can be implied in certain usages of "scoped", "scoped" carries with it extra semantics that "classes by value" on it's own does not. This snippet allows "classes by value" on it's own, which enables different ways of using classes (for example, creating an array of classes by value `Value!MyClass[]`).
I think what Dgame meant was: https://dlang.org/phobos-prerelease/std_typecons#scoped. For the built-in scoped classes, the keyword is 'scope', not 'scoped': https://dlang.org/spec/class.html#auto.
Yes, that's what I meant.
Dec 15 2017