www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Singleton Pattern with struct

reply "ParticlePeter" <ParticlePeter gmx.de> writes:
Hi,

I am trying to figure out the singleton pattern with a struct 
instead of a class:
[code]
struct Singleton  {

private :
	this( int a = 0 ) {} ;
	static Singleton * s ;

public :
	 disable this() ;
	static ref Singleton instance()  {
		if ( s is null )
			s = new Singleton( 0 ) ;
		return * s ;
	}
	
	int val = 0 ;
}
[/code]

This compiles, but when I use it:
[code]
	auto s = Singleton.instance ;
	writeln( s.val ) ;

	Singleton.instance.val = 2 ;
	writeln( s.val ) ;
[/code]

I get:
0
0

Where is my mistake ?

Cheers, PP !
Jan 24 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 24 January 2013 at 14:11:10 UTC, ParticlePeter wrote:
 Hi,

 I am trying to figure out the singleton pattern with a struct 
 instead of a class:
 [code]
 struct Singleton  {

 private :
 	this( int a = 0 ) {} ;
 	static Singleton * s ;

 public :
 	 disable this() ;
 	static ref Singleton instance()  {
 		if ( s is null )
 			s = new Singleton( 0 ) ;
 		return * s ;
 	}
 	
 	int val = 0 ;
 }
 [/code]

 This compiles, but when I use it:
 [code]
 	auto s = Singleton.instance ;
 	writeln( s.val ) ;

 	Singleton.instance.val = 2 ;
 	writeln( s.val ) ;
 [/code]

 I get:
 0
 0

 Where is my mistake ?

 Cheers, PP !
Even if Singleton.instance returns by ref, s object is still stack-allocated struct, which is not affected by further modification of private pointer. import core.stdc.stdio : printf; struct Singleton { private : this( int a = 0 ) {} ; static Singleton * s ; public : disable this() ; static ref Singleton instance() { if ( s is null ) s = new Singleton(0) ; return * s ; } int val = 0 ; } void main() { auto s = Singleton.instance ; printf( "%d\n", s.val ) ; //0 Singleton.instance.val = 2 ; printf( "%d\n", s.val ) ; // also 0 printf( "%d\n", Singleton.instance.val); // is 2 as expected }
Jan 24 2013
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-01-24 15:43, Maxim Fomin wrote:

 Even if Singleton.instance returns by ref, s object is still
 stack-allocated struct, which is not affected by further modification of
 private pointer.
The struct is allocated using "new". -- /Jacob Carlborg
Jan 24 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 24 January 2013 at 15:05:15 UTC, Jacob Carlborg 
wrote:
 On 2013-01-24 15:43, Maxim Fomin wrote:

 Even if Singleton.instance returns by ref, s object is still
 stack-allocated struct, which is not affected by further 
 modification of
 private pointer.
The struct is allocated using "new".
This is about "auto s = " in main, not about "new" in method. Type of s is Singleton, not Singleton* or "ref Singleton". Singleton s = Singleton.instance; // is compiled when Singleton* s = Singleton.instance; // is not - Error: cannot implicitly convert // expression (instance()) of type Singleton to Singleton*
Jan 24 2013
parent reply "ParticlePeter" <ParticlePeter gmx.de> writes:
Got it, thanks, I changed the instance method to:
[code]
	static Singleton * instance()  {
		if ( s is null )
			s = new Singleton( 0 ) ;
		return s ;
	}
[\code]

and everything works as expected.

Cheers, PP !
Jan 24 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 24 January 2013 at 15:50:34 UTC, ParticlePeter wrote:
 Got it, thanks, I changed the instance method to:
 [code]
 	static Singleton * instance()  {
 		if ( s is null )
 			s = new Singleton( 0 ) ;
 		return s ;
 	}
 [\code]

 and everything works as expected.

 Cheers, PP !
Yes, but this can be broken by: import core.stdc.stdio : printf; struct Singleton { private : this( int a = 0 ) {} ; static Singleton * s ; public : disable this() ; static Singleton* instance() { if ( s is null ) s = new Singleton(0) ; return s ; } int val = 0 ; } void main() { Singleton s = * Singleton.instance; printf( "%d\n", s.val ) ; // Singleton.instance.val = 2 ; printf( "%d\n", s.val ) ; //0 } Here s is explicitly defined to be a struct object, not pointer (reference), so main.s is independent of further modification of Singleton.instance.
Jan 24 2013
next sibling parent reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Thursday, 24 January 2013 at 16:07:36 UTC, Maxim Fomin wrote:
 Yes, but this can be broken by:
 void main()
 {
         Singleton s = * Singleton.instance;
         printf( "%d\n", s.val ) ; //
         Singleton.instance.val = 2 ;
         printf( "%d\n", s.val ) ; //0
 }

 Here s is explicitly defined to be a struct object, not pointer 
 (reference), so main.s is independent of further modification 
 of Singleton.instance.
I'm not sure but this seems like one of the few appropriate places i would just make a normal struct and just use a global variable. True there could 'be' multiple instances of these singletons, but only ever making one ensures it would work right so long as you cannot copy it. Wasn't disabling this(this) and opAssign the way to ensure that didn't happen? Course if you didn't and you relied on the instance one, and you cannot copy then you could only pass by reference or be required to use .instance every time you needed it. Seems a bit excessive. Hmmm. You could separate the data and remove the pointer... then use alias this. [code] struct Singleton { static SingletonData single; alias single this; disable this(this); //disable copy/assignment? Not that it would matter... //copying nothing does nothing, as there's nothing here. private static struct SingletonData { //methods and data here. } } [/code]
Jan 24 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 24 January 2013 at 16:17:34 UTC, Era Scarecrow wrote:
 On Thursday, 24 January 2013 at 16:07:36 UTC, Maxim Fomin wrote:
 Yes, but this can be broken by:
 void main()
 {
        Singleton s = * Singleton.instance;
        printf( "%d\n", s.val ) ; //
        Singleton.instance.val = 2 ;
        printf( "%d\n", s.val ) ; //0
 }

 Here s is explicitly defined to be a struct object, not 
 pointer (reference), so main.s is independent of further 
 modification of Singleton.instance.
I'm not sure but this seems like one of the few appropriate places i would just make a normal struct and just use a global variable. True there could 'be' multiple instances of these singletons, but only ever making one ensures it would work right so long as you cannot copy it. Wasn't disabling this(this) and opAssign the way to ensure that didn't happen? Course if you didn't and you relied on the instance one, and you cannot copy then you could only pass by reference or be required to use .instance every time you needed it. Seems a bit excessive. Hmmm. You could separate the data and remove the pointer... then use alias this. [code] struct Singleton { static SingletonData single; alias single this; disable this(this); //disable copy/assignment? Not that it would matter... //copying nothing does nothing, as there's nothing here. private static struct SingletonData { //methods and data here. } [/code]
I agree, but disabling ctors makes creating module-level object tricky. Static struct initialization might help.
Jan 24 2013
parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Thursday, 24 January 2013 at 16:49:53 UTC, Maxim Fomin wrote:
 On Thursday, 24 January 2013 at 16:17:34 UTC, Era Scarecrow 
 wrote:
 Hmmm. You could separate the data and remove the pointer... 
 then use alias this.

 [code]
  struct Singleton {
    static SingletonData single;
    alias single this;
     disable this(this);
    //disable copy/assignment? Not that it would matter...
    //copying nothing does nothing, as there's nothing here.

    private static struct SingletonData {
      //methods and data here.
    }
 [/code]
I agree, but disabling ctors makes creating module-level object tricky. Static struct initialization might help.
I'll have to agree. But in that case don't disable the default ctor (not that the Singleton has any contents anyways); Actually opAssign/postblit only needs to be disabled inside SingletonData. I'd think that would work. [code] struct Singleton { static SingletonData single; alias single this; private static struct SingletonData { disable void opAssign(); //methods and data here. } } [/code]
Jan 24 2013
prev sibling parent reply "ParticlePeter" <ParticlePeter gmx.de> writes:
 Yes, but this can be broken by:

 import core.stdc.stdio : printf;

 struct Singleton  {

 private :
         this( int a = 0 ) {} ;
         static Singleton * s ;

 public :
          disable this() ;
         static Singleton* instance()  {
                 if ( s is null )
                         s = new Singleton(0) ;
                 return s ;
         }

         int val = 0 ;
 }

 void main()
 {
         Singleton s = * Singleton.instance;
         printf( "%d\n", s.val ) ; //
         Singleton.instance.val = 2 ;
         printf( "%d\n", s.val ) ; //0
 }

 Here s is explicitly defined to be a struct object, not pointer 
 (reference), so main.s is independent of further modification 
 of Singleton.instance.
O.k. good to know, I'll try to avoid this. But one thing is still not clear, This method here ( my first approach ) does return a reference to an object on the heap, right ? static ref Singleton instance() { if ( s is null ) s = new Singleton( 0 ) ; return * s ; } so when I use it with: auto another_s = Singleton.instance ; Why is the s inside the struct and another_s not identical ? Afaik that is the purpose of the ref keyword ?
Jan 24 2013
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 01/24/2013 08:52 AM, ParticlePeter wrote:

 This method here ( my first approach ) does return a reference to an
 object on the heap, right ?
Yes, but the caller does not get a reference.
 static ref Singleton instance() {
 if ( s is null )
 s = new Singleton( 0 ) ;
 return * s ;
 }

 so when I use it with:
 auto another_s = Singleton.instance ;

 Why is the s inside the struct and another_s not identical ?
 Afaik that is the purpose of the ref keyword ?
When you print the type of another_s you will see that it is not a ref, because unlike C++, D does not have local ref variables; it has pointers for that purpose. import std.stdio; ref int foo() { return *new int; } void main() { auto i = foo(); writeln(typeid(i)); } Prints 'int', not 'ref int'. So, i is a copy of the dynamically created int. Ali
Jan 24 2013
next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 24 January 2013 at 17:00:44 UTC, Ali Çehreli wrote:
 On 01/24/2013 08:52 AM, ParticlePeter wrote:

 This method here ( my first approach ) does return a
reference to an
 object on the heap, right ?
Yes, but the caller does not get a reference.
Actually the caller gets the reference (which is a pointer from low-level POV), but allocates on stack struct object, and then does copy from returned reference to that stack object.
 static ref Singleton instance() {
 if ( s is null )
 s = new Singleton( 0 ) ;
 return * s ;
 }

 so when I use it with:
 auto another_s = Singleton.instance ;

 Why is the s inside the struct and another_s not identical ?
 Afaik that is the purpose of the ref keyword ?
When you print the type of another_s you will see that it is not a ref, because unlike C++, D does not have local ref variables; it has pointers for that purpose.
Yes, that the point - D does not have references like C++. And I should thought about C++ influence on understanding D :)
 import std.stdio;

 ref int foo()
 {
     return *new int;
 }

 void main()
 {
     auto i = foo();
     writeln(typeid(i));
 }

 Prints 'int', not 'ref int'. So, i is a copy of the dynamically 
 created int.

 Ali
Jan 24 2013
prev sibling parent reply "ParticlePeter" <ParticlePeter gmx.de> writes:
On Thursday, 24 January 2013 at 17:00:44 UTC, Ali Çehreli wrote:
 On 01/24/2013 08:52 AM, ParticlePeter wrote:

 This method here ( my first approach ) does return a
reference to an
 object on the heap, right ?
Yes, but the caller does not get a reference.
 static ref Singleton instance() {
 if ( s is null )
 s = new Singleton( 0 ) ;
 return * s ;
 }

 so when I use it with:
 auto another_s = Singleton.instance ;

 Why is the s inside the struct and another_s not identical ?
 Afaik that is the purpose of the ref keyword ?
When you print the type of another_s you will see that it is not a ref, because unlike C++, D does not have local ref variables; it has pointers for that purpose. import std.stdio; ref int foo() { return *new int; } void main() { auto i = foo(); writeln(typeid(i)); } Prints 'int', not 'ref int'. So, i is a copy of the dynamically created int. Ali
Thanks, I re-read the purpose of ref type function() in the D programming language, and the sole purpose is that such a function call can be directly a parameter to another function expecting a ref ? As: ref int foo() { return some class member ; } void bar( ref int data ) { do something with data ; } This means, it is never ever possible to initialize any variable with a reference some class/struct member data ? Unless I return the address of the member data ?
Jan 24 2013
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 01/24/2013 09:26 AM, ParticlePeter wrote:

 Thanks, I re-read the purpose of ref type function() in the D
 programming language, and the sole purpose is that such a function call
 can be directly a parameter to another function expecting a ref ?
As Maxim Fomin noted, I didn't word it correctly: The caller does get a reference to the returned object. So, the sole purpose is not to pass a variable to a ref-taking function.
 As:

 ref int foo() { return some class member ; }
 void bar( ref int data ) { do something with data ; }

 This means, it is never ever possible to initialize any variable with a
 reference some class/struct member data ? Unless I return the address of
 the member data ?
Not true. There are no local ref variables nor ref member variables in D. All you need to do is to use pointers instead: ref int foo() { return *new int; } struct S { int i; int * j; this(int i) { this.i = i; this.j = &foo(); // member pointer } } void main() { int* i = &foo(); // local pointer } No, the pointer syntax is not the cleanest. :) Ali
Jan 24 2013
parent "ParticlePeter" <ParticlePeter gmx.de> writes:
On Thursday, 24 January 2013 at 17:35:58 UTC, Ali Çehreli wrote:
 On 01/24/2013 09:26 AM, ParticlePeter wrote:

 Thanks, I re-read the purpose of ref type function() in the D
 programming language, and the sole purpose is that such a
function call
 can be directly a parameter to another function expecting a
ref ? As Maxim Fomin noted, I didn't word it correctly: The caller does get a reference to the returned object. So, the sole purpose is not to pass a variable to a ref-taking function.
 As:

 ref int foo() { return some class member ; }
 void bar( ref int data ) { do something with data ; }

 This means, it is never ever possible to initialize any
variable with a
 reference some class/struct member data ? Unless I return the
address of
 the member data ?
Not true. There are no local ref variables nor ref member variables in D. All you need to do is to use pointers instead: ref int foo() { return *new int; } struct S { int i; int * j; this(int i) { this.i = i; this.j = &foo(); // member pointer } } void main() { int* i = &foo(); // local pointer } No, the pointer syntax is not the cleanest. :) Ali
This is what I meant :-) I can't return a reference ( with reference I don't mean reference type, but semantically a reference ) to a class member, but I can return the address of this member, which, to my understanding is an implicit pointer. struct Foo { int val = 3 ; auto getValPtr() { return & val ; } } Foo foo ; writeln( foo.val ) ; // = 3 auto valPtr = foo.getValPtr() ; * valPtr = 7 ; writeln( foo.val ) ; // = 7
Jan 24 2013
prev sibling next sibling parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 01/24/13 17:52, ParticlePeter wrote:
 Yes, but this can be broken by:

 import core.stdc.stdio : printf;

 struct Singleton  {

 private :
         this( int a = 0 ) {} ;
         static Singleton * s ;

 public :
          disable this() ;
         static Singleton* instance()  {
                 if ( s is null )
                         s = new Singleton(0) ;
                 return s ;
         }

         int val = 0 ;
 }

 void main()
 {
         Singleton s = * Singleton.instance;
         printf( "%d\n", s.val ) ; //
         Singleton.instance.val = 2 ;
         printf( "%d\n", s.val ) ; //0
 }

 Here s is explicitly defined to be a struct object, not pointer (reference),
so main.s is independent of further modification of Singleton.instance.
O.k. good to know, I'll try to avoid this. But one thing is still not clear, This method here ( my first approach ) does return a reference to an object on the heap, right ? static ref Singleton instance() { if ( s is null ) s = new Singleton( 0 ) ; return * s ; } so when I use it with: auto another_s = Singleton.instance ; Why is the s inside the struct and another_s not identical ? Afaik that is the purpose of the ref keyword ?
There currently are no reference variables in D [1]; the only way to get a reference to something is via function arguments (including implicit method ones) and function returns. So assigning a ref-return means a copy. You can workaround it like this: struct Singleton { private: this( int a = 0 ) {} ; static Singleton* s ; public: disable this(); disable this(this); static instance() property { static struct Ref(T) { T* obj; ref g() property { return *obj; } alias obj this; disable this(this); } if ( s is null ) s = new Singleton( 0 ) ; return Ref!(typeof(this))(s) ; } int val = 0 ; } though, that's not how I'd do it. artur [1] Classes don't count; they are a reference /types/.
Jan 24 2013
parent reply "ParticlePeter" <ParticlePeter gmx.de> writes:
On Thursday, 24 January 2013 at 17:21:38 UTC, Artur Skawina wrote:
 On 01/24/13 17:52, ParticlePeter wrote:
 Yes, but this can be broken by:

 import core.stdc.stdio : printf;

 struct Singleton  {

 private :
         this( int a = 0 ) {} ;
         static Singleton * s ;

 public :
          disable this() ;
         static Singleton* instance()  {
                 if ( s is null )
                         s = new Singleton(0) ;
                 return s ;
         }

         int val = 0 ;
 }

 void main()
 {
         Singleton s = * Singleton.instance;
         printf( "%d\n", s.val ) ; //
         Singleton.instance.val = 2 ;
         printf( "%d\n", s.val ) ; //0
 }

 Here s is explicitly defined to be a struct object, not 
 pointer (reference), so main.s is independent of further 
 modification of Singleton.instance.
O.k. good to know, I'll try to avoid this. But one thing is still not clear, This method here ( my first approach ) does return a reference to an object on the heap, right ? static ref Singleton instance() { if ( s is null ) s = new Singleton( 0 ) ; return * s ; } so when I use it with: auto another_s = Singleton.instance ; Why is the s inside the struct and another_s not identical ? Afaik that is the purpose of the ref keyword ?
There currently are no reference variables in D [1]; the only way to get a reference to something is via function arguments (including implicit method ones) and function returns. So assigning a ref-return means a copy. You can workaround it like this: struct Singleton { private: this( int a = 0 ) {} ; static Singleton* s ; public: disable this(); disable this(this); static instance() property { static struct Ref(T) { T* obj; ref g() property { return *obj; } alias obj this; disable this(this); } if ( s is null ) s = new Singleton( 0 ) ; return Ref!(typeof(this))(s) ; } int val = 0 ; } though, that's not how I'd do it. artur [1] Classes don't count; they are a reference /types/.
Well ... I think I as well would not wanna do it like this, thanks :-) I'm fine with returning and using a pointer, fortunately there is no difference in syntax as in c, so it doesn't matter.
Jan 24 2013
next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 01/24/2013 09:35 AM, ParticlePeter wrote:

 I'm fine with returning and using a pointer, fortunately there is no
 difference in syntax as in c, so it doesn't matter.
With the exception that the -> operator is replaced by the dot operator in D: MyStruct * p = /* ... */; // Dot, not -> p.foo(); p.i = 42; Ali
Jan 24 2013
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 01/24/13 18:35, ParticlePeter wrote:
 On Thursday, 24 January 2013 at 17:21:38 UTC, Artur Skawina wrote:
 On 01/24/13 17:52, ParticlePeter wrote:
 Why is the s inside the struct and another_s not identical ?
 Afaik that is the purpose of the ref keyword ?
There currently are no reference variables in D [1]; the only way to get a reference to something is via function arguments (including implicit method ones) and function returns. So assigning a ref-return means a copy. You can workaround it like this: struct Singleton { private: this( int a = 0 ) {} ; static Singleton* s ; public: disable this(); disable this(this); static instance() property { static struct Ref(T) { T* obj; ref g() property { return *obj; } alias g this; disable this(this); } if ( s is null ) s = new Singleton( 0 ) ; return Ref!(typeof(this))(s) ; } int val = 0 ; }
 Well ... I think I as well would not wanna do it like this, thanks :-)
 I'm fine with returning and using a pointer, fortunately there is no
difference in syntax as in c, so it doesn't matter.
Careful, at some point you'll end up doing s[1] etc, and /then/ it matters. The Ref struct is actually the way to deal with "ref" types in D; what I meant by 'that's not how I'd do it' is that it might be better to use a global (well, thread-local module-field in D) etc. artur
Jan 24 2013
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 01/24/13 18:14, Artur Skawina wrote:
 struct Singleton  {
 private:
     this( int a = 0 ) {} ;
     static Singleton* s ;
 
 public:
      disable this();
      disable this(this);
     static instance()  property {
         static struct Ref(T) { T* obj; ref g()  property { return *obj; }
alias obj this;  disable this(this); }
         if ( s is null )
             s = new Singleton( 0 ) ;
         return Ref!(typeof(this))(s) ;
     }
     
     int val = 0 ;
 }
Grr. What I meant to write was of course: static struct Ref(T) { T* obj; ref g() property { return *obj; } alias g this; disable this(this); } Sorry, artur
Jan 24 2013
prev sibling parent "ParticlePeter" <ParticlePeter gmx.de> writes:
 Even if Singleton.instance returns by ref, s object is still 
 stack-allocated struct, which is not affected by further 
 modification of private pointer.
But where and when is ( a second ? ) Singleton created or duplicated ? The only ctor is in the static instance ( it is called only once according to my debugger ) and allocates a Singleton instance on the Heap ( as far as I understand "new Struct" ). The return statement does return a reference ( to a pointer, even that shouldn't be necessary ) so I would expect ... well a reference and not a copy. So where does the second object come from ?
Jan 24 2013