Thread overview
Q: How can I make a class act like a value type (such as struct)
May 27, 2007
Myron Alexander
May 27, 2007
Regan Heath
May 27, 2007
Myron Alexander
May 28, 2007
Bill Baxter
May 28, 2007
Myron Alexander
May 27, 2007
Myron Alexander
May 27, 2007
Carlos Santander
May 27, 2007
Myron Alexander
May 27, 2007
Hello.

I currently have a struct for named parameters called NamedParameter and this is defined as such:

struct NamedParameter {
   char[] name;
   Box    value;

   char[] toString () {
      return "(" ~ name ~ ": " ~ value.toString () ~ ")";
   }

   static NamedParameter opCall(T) (char[] name, T value) {

      // I have used a static if instead of static assert as the assert cannot
      // resolve and output the name and value.name. I would prefer a compile
      // time error.
      static if (is (T == NamedParameter)) {
         throw new SqlProgrammingException (
            "Invalid named parameter value. The value of a named parameter may "
            "not be a named parameter. Error on parameter (" ~ name ~
            ") trying to nest parameter (" ~ value.name ~
            ").");
      }

      NamedParameter m_np;

      m_np.name  = name;

      static if (is (T == Box)) {
         m_np.value = value;

      } else {
         m_np.value = box (value);
      }

      return m_np;
   }
}


The problem with this struct is that the name, and value fields are public and I want them private so the user is forced through the opCall. My reason for this is that I want all the validations done on initialization instead of having to be done whenever I use the values. (These validations are not in the above code).

The only way to make the fields private is to have a class so I would like to build my class to work as a value object in the same way that structs do but with information hiding.

How can I do this?

Thanks ahead,

Myron.
May 27, 2007
Myron Alexander Wrote:
> The only way to make the fields private is to have a class so I would like to build my class to work as a value object in the same way that structs do but with information hiding.

What 'value object' behaviour do you want?  As in, how are you using these types?  Example please :)  Something like this...

NamedParameter p("test",5);
NamedParameter q;

q = p;

In the above, is it that you want q to be a copy of p as opposed to both p and q referring to the same object?

If so, D does not allow you to overload assignment and instead you would need to use a copy method or perhaps supply a constructor that copies, eg.

class NamedParameter {
 NamedParameter(NamedParameter rhs) { ..copy rhs to self here.. }
 NamedParameter dup() { ..create new copy of self and return it.. }
}

If you want some other value object behaviour please explain.

Regan Heath
May 27, 2007
Myron Alexander escribió:
> 
> The problem with this struct is that the name, and value fields are public and I want them private so the user is forced through the opCall. My reason for this is that I want all the validations done on initialization instead of having to be done whenever I use the values. (These validations are not in the above code).
> 
> The only way to make the fields private is to have a class so I would like to build my class to work as a value object in the same way that structs do but with information hiding.
> 

You can use "private" et al. with structs too.

-- 
Carlos Santander Bernal
May 27, 2007
Regan Heath wrote:
> What 'value object' behaviour do you want?  As in, how are you using these types?  Example please :)  Something like this...
> 
> NamedParameter p("test",5);
> NamedParameter q;
> 
> q = p;
> 
> In the above, is it that you want q to be a copy of p as opposed to both p and q referring to the same object?
> 

Regan,

The above is one aspect of what I meant as a value object but my manner of use does not need it. I am more concerned with storage and I want my class to be allocated on the stack rather than on the heap so that the GC does not have to bother with it.

If such a thing can't be done, then I am happy with GC'd references.

This is a simple example of how it is used (np is an alias for NamedParameter):

cu.execute (
  "insert into atable values (:id, :name, :balance)",
  np("id",4), np("name","Fourth Person"), np("balance",999.99));


I've done a conversion and this is my latest version:

class NamedParameter {

   private this (char[] name, Box value) {
      m_name  = name;
      m_value = value;
   }

   static NamedParameter opCall(T) (char[] name, T value) {

      // I have used a static if instead of static assert as the assert cannot
      // resolve name and value.name. I would prefer a compile time error.
      static if (is (T == NamedParameter)) {
         throw new SqlProgrammingException (
            "Invalid named parameter value. The value of a named parameter may "
            "not be a named parameter. Error on parameter (" ~ name ~
            ") trying to nest parameter (" ~ value.name ~
            ").");
      }

      Box v;

      static if (is (T == Box)) {
         /* Unnest the innermost value.
          */
         v = value;
         while (v.type == typeid (Box)) {
            v = unbox!(Box)(v);
         }

      } else {
         v = box (value);
      }

      return new NamedParameter (name, v);
   }

   char[] name () {
      return m_name;
   }

   Box value () {
      return m_value;
   }

   char[] toString () {
      return "(" ~ m_name ~ ": " ~ m_value.toString () ~ ")";
   }

   private char[] m_name;
   private Box    m_value;
}

Regards,

Myron.
May 27, 2007
Carlos Santander wrote:
> You can use "private" et al. with structs too.
> 

Carlos,

I did not know that. The documentation says that hidden members are not a feature of structs, I took it to mean that private does not work. Having a DOH moment :)

Thanks.

Best regards,

Myron.
May 27, 2007
Regan,

Thanks for the help. I have solved my problem by using private with struct.

Best regards,

Myron.
May 28, 2007
Myron Alexander wrote:
> Regan Heath wrote:

> The above is one aspect of what I meant as a value object but my manner of use does not need it. I am more concerned with storage and I want my class to be allocated on the stack rather than on the heap so that the GC does not have to bother with it.

You seem to have solved your problem using structs w/ private, but just FYI you can make classes be allocated on the stack using the 'scope' keyword.

 void something()
 {
    auto foo = new SomeClass(); // foo on the heap
    scope bar = new SomeClass(); // bar on the stack
    . . .

 }

And I think if you put scope in the class declaration it makes you use 'scope' every time you use the class (? I think...):

scope class SomeClass()
{
   . . .
}

I tried that once long ago, but never have been able to think of a case where that would be better than just using a struct.  I guess if you want it to implement an interface...

--bb
May 28, 2007
Bill Baxter wrote:
> You seem to have solved your problem using structs w/ private, but just FYI you can make classes be allocated on the stack using the 'scope' keyword.
> 
>  void something()
>  {
>     auto foo = new SomeClass(); // foo on the heap
>     scope bar = new SomeClass(); // bar on the stack
>     . . .
> 
>  }
> 
> And I think if you put scope in the class declaration it makes you use 'scope' every time you use the class (? I think...):
> 
> scope class SomeClass()
> {
>    . . .
> }
> 
> I tried that once long ago, but never have been able to think of a case where that would be better than just using a struct.  I guess if you want it to implement an interface...
> 
> --bb

For my library, I use Box to pass values around. My simple test seems to show that a scope class can be assigned to a box. The only functionality I cannot provide with scope classes is the opCall that I am using.

I whipped up a simple test:

import std.stdio;
import std.boxer;

scope class Xx {
   this (char[] v = "Default") {
      value = v;
   }

   //~ static Xx opCall (char[] v) {
   //~    return new Xx(v);
   //~ }

   private char[] value;

   char[] toString () {
      return value;
   }
}

void dobe (Box b) {
   writefln ("%s", b);
}

void main () {
   scope x = new Xx();

   Box y = box (x);

   dobe (y);
}

If I uncomment the opCall, I get the following compiler error:
scope_box.d(9): Error: functions cannot return auto scope_box.Xx

Luckily, the struct works for my case so I am ok :) I also heard that structs are getting constructors which is double bonus.

Regards,

Myron.