www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Box data type

reply Derek Parnell <derek psych.ward> writes:
Why is a Box a struct and not a class object?

It sure makes generic coding more difficult.

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
29/09/2005 4:31:23 PM
Sep 28 2005
parent reply Burton Radons <burton-radons smocky.com> writes:
Derek Parnell wrote:
 Why is a Box a struct and not a class object?

"there aren't any real opportunities for overloading, it's not mutable anyway, and many people will be repelled by the allocation". Boxing necessarily resulting in an allocation is one of the reasons people reject C# out-of-hand.
 It sure makes generic coding more difficult.

Could you give an example? I can't even begin to say anything without knowing what you're trying to do.
Sep 29 2005
parent reply Derek Parnell <derek psych.ward> writes:
On Thu, 29 Sep 2005 13:13:37 -0700, Burton Radons wrote:

 Derek Parnell wrote:
 Why is a Box a struct and not a class object?

"there aren't any real opportunities for overloading, it's not mutable anyway, and many people will be repelled by the allocation". Boxing necessarily resulting in an allocation is one of the reasons people reject C# out-of-hand.

Okay, that makes good sense.
 It sure makes generic coding more difficult.

Could you give an example? I can't even begin to say anything without knowing what you're trying to do.

I was playing around with the reference counting class that Regan Heath supplied in an earlier post. In that case, the only things that could be reference-counted were class instances. In order to count strings or numbers I first tried to 'box' them without realizing that Box was a struct. Of course that failed. Modifying Regan's code to allow things other than objects was a PITA. -- Derek Parnell Melbourne, Australia 30/09/2005 6:39:35 AM
Sep 29 2005
parent reply Larry Evans <cppljevans cos-internet.com> writes:
On 09/29/2005 03:46 PM, Derek Parnell wrote:
[snip]
 I was playing around with the reference counting class that Regan Heath
 supplied in an earlier post. In that case, the only things that could be

 

Would this class contradict the statement: it is impossible to implement a useful smart_ptr in D. from: digitalmars.D/29135 ?
Oct 15 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
------------craGqMgVCzdYtKHv5vMksu
Content-Type: text/plain; format=flowed; delsp=yes; charset=iso-8859-15
Content-Transfer-Encoding: 8bit

On Sat, 15 Oct 2005 16:38:04 -0500, Larry Evans  
<cppljevans cos-internet.com> wrote:
 On 09/29/2005 03:46 PM, Derek Parnell wrote:
 [snip]
 I was playing around with the reference counting class that Regan Heath
 supplied in an earlier post. In that case, the only things that could be



Attached.
 Would this class contradict the statement:

    it is impossible to implement a useful smart_ptr in D.

 from:

    digitalmars.D/29135

 ?

That's debatable. It does 'work' but it's not as "fool proof" as you'd like it to be, in other words it is more likely you (by this I mean anyone using it) will accidently missuse it than a version written in say C++ (which allows you you overload the assignment operator) For example, you must remember to 'assign' a new RefPtr with: RefPtr p = new RefPtr(old_one); you cannot simply say: RefPtr p = old_one; which means that when adding RefPtr's to an existing app you have to find all assignments of all references to the shared resource and replace them with constructor calls like those above. This makes it significantly more work, and significantly more prone to error. Regan ------------craGqMgVCzdYtKHv5vMksu Content-Disposition: attachment; filename=refcnt.d Content-Type: application/octet-stream; name=refcnt.d Content-Transfer-Encoding: 8bit import std.c.windows.windows; import std.random; import std.thread; import std.stdio; class RefPtr { RefPtr parent; Object resource = null; int refs = 0; this(Object res) { resource = res; writefln("ThreadID=(",Thread.getThis().id,") Initial RefPtr for resource=(",resource,")"); increment(); } this(RefPtr rhs) { parent = rhs; parent.increment(); writefln("ThreadID=(",Thread.getThis().id,") Ref=(",parent.refs,") for resource=(",parent.resource,")"); } ~this() { int r; if ((r = decrement()) == 0) { writefln("ThreadID=(",Thread.getThis().id,") release last ref Ref=(",r,")"); if (parent) parent = null; else if (resource) { writefln("ThreadID=(",Thread.getThis().id,") delete resource=(",resource,")"); delete resource; resource = null; } } writefln("ThreadID=(",Thread.getThis().id,") release Ref=(",r,")"); } protected: int increment() { int ret; if (parent) ret = parent.increment(); else { synchronized(this) { ret = ++refs; writefln("ThreadID=(",Thread.getThis().id,") increment to Ref=(",refs,")"); } } return ret; } int decrement() { int ret; if (parent) ret = parent.decrement(); else { synchronized(this) { ret = --refs; writefln("ThreadID=(",Thread.getThis().id,") decrement to Ref=(",refs,")"); } } return ret; } } class Resource { char[] name = "11 was a racehorse"; char[] toString() { return name; } } RefPtr pbob; static this() { pbob = new RefPtr(new Resource()); } static ~this() { delete pbob; } void main() { Thread[] threads; int i; writefln("ThreadID=(",Thread.getThis().id,") is the main thread"); for(i = 0; i < 10; i++) { threads ~= new Thread(&thread_function,null); threads[$-1].start(); } while(true) { i = 0; foreach(Thread t; threads) { if (t.getState() == Thread.TS.TERMINATED) i++; } if (i == 10) break; Sleep(100); } writefln("Main exiting"); } int thread_function(void* isnull) { auto RefPtr p = new RefPtr(pbob); Sleep(1000+rand()%1000); return 0; } ------------craGqMgVCzdYtKHv5vMksu--
Oct 15 2005
parent reply Larry Evans <cppljevans cos-internet.com> writes:
On 10/15/2005 05:05 PM, Regan Heath wrote:
 On Sat, 15 Oct 2005 16:38:04 -0500, Larry Evans  

 How can I find Regan's reference counting class, please?

Attached.

Thanks! [snip]
 anyone  using it) will accidently missuse it than a version written in 
 say  C++ (which allows you you overload the assignment operator)
 
 For example, you must remember to 'assign' a new RefPtr with:
 
   RefPtr p = new RefPtr(old_one);
 
 you cannot simply say:
 
   RefPtr p = old_one;
 
 which means that when adding RefPtr's to an existing app you have to 
 find  all assignments of all references to the shared resource and 
 replace them  with constructor calls like those above. This makes it 
 significantly more  work, and significantly more prone to error.
 

There are two questions I have about refcnt.d: 1) The reference count, declared as: int refs = 0; at refcnt.d:9, is part of the RefPtr, not the resource: Object resource = null; at recfnt.d:10. Hence, it's a detached refcount. However, it's declared as a non-pointer type, and so it cannot be shared between 2 RefPtr's pointing to the same resource. What am I missing? 2) There's the RefPtr field: RefPtr parent; What is the purpose of parent? I can't think of a counterpart in c++ code. Thanks for any clarification. -best regards, Larry
Oct 17 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Mon, 17 Oct 2005 12:21:38 -0500, Larry Evans  
<cppljevans cos-internet.com> wrote:
 On 10/15/2005 05:05 PM, Regan Heath wrote:
 On Sat, 15 Oct 2005 16:38:04 -0500, Larry Evans

 How can I find Regan's reference counting class, please?


Thanks! [snip]
 anyone  using it) will accidently missuse it than a version written in  
 say  C++ (which allows you you overload the assignment operator)
  For example, you must remember to 'assign' a new RefPtr with:
    RefPtr p = new RefPtr(old_one);
  you cannot simply say:
    RefPtr p = old_one;
  which means that when adding RefPtr's to an existing app you have to  
 find  all assignments of all references to the shared resource and  
 replace them  with constructor calls like those above. This makes it  
 significantly more  work, and significantly more prone to error.

There are two questions I have about refcnt.d: 1) The reference count, declared as: int refs = 0; at refcnt.d:9, is part of the RefPtr, not the resource: Object resource = null; at recfnt.d:10. Hence, it's a detached refcount.

The reference count is not part of the resource because that would require all countable resources to be derived from a common base, D has single inheritance so this would be very limiting. The idea was to keep the reference couting seperate from the resource itself. It is likely possible to create a mixin of some sort and derive a countable class from the class you want to count using the mixin. I am not sure what the benefit of that might be.
 However, it's declared as a non-pointer type, and
 so it cannot be shared between 2 RefPtr's pointing
 to the same resource.  What am I missing?

The count is shared using the 'parent' reference. This is why you must create a RefCnt using an existing RefCnt (and not the resource itself) in all cases besides the first. That is why I used a static ctor to create the initial RefCnt, because that must be used in place of the resource for the rest of the program.
 2) There's the RefPtr field:

     RefPtr parent;

 What is the purpose of parent?  I can't think of
 a counterpart in c++ code.

There likely isn't. This was the only way I could think of to share the reference count and ensure it was incremented and decremented at the appropriate times. Note also 'auto' or explicit delete is required on all RefCnt references or the count will not be decremented when that reference is collected by the GC. It is possible my implementation is naive. To be honest I am not an expert on reference counting, I created this as an example of what was possible in D, to show how deficient D was in this area. Regan
Oct 17 2005
parent reply Larry Evans <cppljevans cos-internet.com> writes:
On 10/17/2005 04:01 PM, Regan Heath wrote:
 On Mon, 17 Oct 2005 12:21:38 -0500, Larry Evans  

 The reference count is not part of the resource because that would 
 require  all countable resources to be derived from a common base, D has 
 single  inheritance so this would be very limiting. The idea was to keep 
 the  reference couting seperate from the resource itself. It is likely 
 possible  to create a mixin of some sort and derive a countable class 
 from the class  you want to count using the mixin. I am not sure what 
 the benefit of that  might be.

int refs = 0; be changed to: int* refs = null; Then, change this(RefPtr rhs) to: this(RefPtr rhs) { resource = rhs.resource; refs = rhs.refs; (*refs)++; } and eliminate: RefPtr parent; altogether?
Oct 18 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Tue, 18 Oct 2005 11:32:49 -0500, Larry Evans  
<cppljevans cos-internet.com> wrote:
 On 10/17/2005 04:01 PM, Regan Heath wrote:
 On Mon, 17 Oct 2005 12:21:38 -0500, Larry Evans

 The reference count is not part of the resource because that would  
 require  all countable resources to be derived from a common base, D  
 has single  inheritance so this would be very limiting. The idea was to  
 keep the  reference couting seperate from the resource itself. It is  
 likely possible  to create a mixin of some sort and derive a countable  
 class from the class  you want to count using the mixin. I am not sure  
 what the benefit of that  might be.

int refs = 0; be changed to: int* refs = null; Then, change this(RefPtr rhs) to: this(RefPtr rhs) { resource = rhs.resource; refs = rhs.refs; (*refs)++; } and eliminate: RefPtr parent; altogether?

Perhaps. However, you need to synchronize access to that integer. My code synchronizes on the class, you would have to use some sort of InterlockedIncrement function or similar. Regan
Oct 18 2005
parent reply Larry Evans <cppljevans cos-internet.com> writes:
On 10/18/2005 03:08 PM, Regan Heath wrote:
 On Tue, 18 Oct 2005 11:32:49 -0500, Larry Evans  

 Couldn't:

    int refs = 0;

 be changed to:

    int* refs = null;


 and eliminate:

    RefPtr parent;

 altogether?

Perhaps. However, you need to synchronize access to that integer. My code synchronizes on the class, you would have to use some sort of InterlockedIncrement function or similar.

Ah! I hadn't considered synchronization. But then couldn't the refs be encapsulated in another class to do this: class RefPtr { RefPtr parent; Object resource = null; class RefCount { int num_refs = 0; int increment() { //sychronized increment } int decrement() { //synchronized decrement } }//end RefCount class RefCount refs = null; ... }//end RefPtr class OK, now I'm probably showing my ignorance of how D differs from C++. My understanding is that, since RefCount is a class, it's allocated on the heap. Also, based on the original code, which contained: resource = res; I'm assuming that the assert in the following code passes: this(RefPtr rhs) { resource = rhs.resource; refs = rhs.refs; refs.increment(); assert(refs is rhs.refs); } IOW, the reference count is shared between rhs and this.
Oct 19 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 19 Oct 2005 10:33:07 -0500, Larry Evans  
<cppljevans cos-internet.com> wrote:
 On 10/18/2005 03:08 PM, Regan Heath wrote:
 On Tue, 18 Oct 2005 11:32:49 -0500, Larry Evans

 Couldn't:

    int refs = 0;

 be changed to:

    int* refs = null;


 and eliminate:

    RefPtr parent;

 altogether?

code synchronizes on the class, you would have to use some sort of InterlockedIncrement function or similar.

Ah! I hadn't considered synchronization. But then couldn't the refs be encapsulated in another class to do this:

What advantage do you see in using 'another' class as opposed to doing what I did?
 class RefPtr
 {
    RefPtr parent;
    Object resource = null;

    class RefCount
    {
      int num_refs = 0;

      int increment()
      {
         //sychronized increment
      }

      int decrement()
      {
         //synchronized decrement
      }


    }//end RefCount class

    RefCount refs = null;

    ...

 }//end RefPtr class

The only difference I see is that the shared reference count no longer contains the reference to resource. To my mind the count and the reference to the resource belong together. That said, my implementation did have an extra reference to the resource hanging around.
 OK, now I'm probably showing my ignorance of how D differs from C++.  My
 understanding is that, since RefCount is a class, it's allocated on
 the heap.

Correct.
 Also, based on the original code, which contained:

    resource = res;

 I'm assuming that the assert in the following code passes:

    this(RefPtr rhs)
    {
      resource = rhs.resource;
      refs = rhs.refs;
      refs.increment();
      assert(refs is rhs.refs);
    }

Yes, it should.
 IOW, the reference count is shared between rhs and this.

Yes. Regan
Oct 19 2005
next sibling parent reply Larry Evans <cppljevans cos-internet.com> writes:
On 10/19/2005 03:43 PM, Regan Heath wrote:
 On Wed, 19 Oct 2005 10:33:07 -0500, Larry Evans  

 Ah!  I hadn't considered synchronization. But then couldn't the
 refs be encapsulated in another class to do this:

What advantage do you see in using 'another' class as opposed to doing what I did?

initially, about the purpose of parent. Maybe my OOPS (see next few lines) obscured this advantage.
 
 class RefPtr
 {
    RefPtr parent;


    Object resource = null;

    class RefCount
    {
      int num_refs = 0;

      int increment()
      {
         //sychronized increment
      }

      int decrement()
      {
         //synchronized decrement
      }


    }//end RefCount class

    RefCount refs = null;

    ...

 }//end RefPtr class

The only difference I see is that the shared reference count no longer contains the reference to resource. To my mind the count and the reference to the resource belong together.

Well, they are, in RefPtr. OTOH, I can see where you could put them together in RefCount and rename it to something like ResourceCount, which would essentially be the type of your parent. Then you could just remove RefPtr.resource since it's now RefPtr.refs.resource. But then that's an extra indirection which, AFAICT, has no advantage.
 That said, my implementation 
 did have an  extra reference to the resource hanging around.

And that extra reference was confusing to me as well as taking extra space.
Oct 19 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 19 Oct 2005 16:06:02 -0500, Larry Evans  
<cppljevans cos-internet.com> wrote:
 On 10/19/2005 03:43 PM, Regan Heath wrote:
 On Wed, 19 Oct 2005 10:33:07 -0500, Larry Evans

 Ah!  I hadn't considered synchronization. But then couldn't the
 refs be encapsulated in another class to do this:

doing what I did?


 It makes the purpose of the RefPtr members clearer.  I was confused,
 initially, about the purpose of parent.  Maybe my OOPS (see next few
 lines) obscured this advantage.

Nah. I would re-organise as follows:
 class RefPtr
 {
    class ResCount
    {
      Object resource = null;
      int num_refs = 0;

      int increment()
      {
         //sychronized increment
      }

      int decrement()
      {
         //synchronized decrement
      }
    }//end ResCount class

    ResCount ref = null;

    ...

 }//end RefPtr class

longer contains the reference to resource. To my mind the count and the reference to the resource belong together.

Well, they are, in RefPtr. OTOH, I can see where you could put them together in RefCount and rename it to something like ResourceCount, which would essentially be the type of your parent. Then you could just remove RefPtr.resource since it's now RefPtr.refs.resource. But then that's an extra indirection which, AFAICT, has no advantage.

Indeed, I have done just that above.
 That said, my implementation did have an  extra reference to the  
 resource hanging around.

And that extra reference was confusing to me as well as taking extra space.

True. Regan
Oct 19 2005
prev sibling parent reply Larry Evans <cppljevans cos-internet.com> writes:
On 10/19/2005 03:43 PM, Regan Heath wrote:
 On Wed, 19 Oct 2005 10:33:07 -0500, Larry Evans  

 I'm assuming that the assert in the following code passes:

    this(RefPtr rhs)
    {
      resource = rhs.resource;
      refs = rhs.refs;
      refs.increment();
      assert(refs is rhs.refs);
    }

Yes, it should.
 IOW, the reference count is shared between rhs and this.

Yes.

Now, I'm guessing a "deep copy" of the resource would be is done with: resource = new Object(rhs.resource) although this isn't relevant to the current discusssion about smart pointers. I'm just checking my understanding. Or maybe the above just does a "level-1" copy. Maybe nows the time to actually try some D and see ;)
Oct 19 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 19 Oct 2005 16:22:56 -0500, Larry Evans  
<cppljevans cos-internet.com> wrote:
 On 10/19/2005 03:43 PM, Regan Heath wrote:
 On Wed, 19 Oct 2005 10:33:07 -0500, Larry Evans

 I'm assuming that the assert in the following code passes:

    this(RefPtr rhs)
    {
      resource = rhs.resource;
      refs = rhs.refs;
      refs.increment();
      assert(refs is rhs.refs);
    }

 IOW, the reference count is shared between rhs and this.


Now, I'm guessing a "deep copy" of the resource would be is done with: resource = new Object(rhs.resource)

No. D has no built in deep copy functionality. Instead it's customary to define a 'dup' method the class, so the above would read: resource = rhs.resource.dup();
 although this isn't relevant to the current discusssion about
 smart pointers.  I'm just checking my understanding.  Or maybe
 the above just does a "level-1" copy.  Maybe nows the time
 to actually try some D and see ;)

Sounds like a plan. :) Regan
Oct 19 2005