www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - class allocators should be more encapsulated

reply =?ISO-8859-1?Q?Lu=EDs_Marques?= <luismarques gmail.com> writes:
Hello all.

I need to build a class for which there should be only one instance with 
a given attribute (of type char[]).

That can be provided by a class allocator with the following form:

     new (uint size, char[] data)
     {
         ...
     }

The same "data" must then also be a constructor parameter:

     this(char[] data)
     {
         this.data = data;
         ...
     }

Allocations then take the form:

new("my string") ClassType("my string");

That is problem #1, having to repeat the string. We could wrap this in a 
static method or in a template, but that takes away the point of a 
customized new: we are mostly back to the C++ method of having a private 
constructor and providing a factory method.

The problem #2 is repetition of calculations. All calculations that 
new() does cannot be stored in the instance, so this() has to perform 
them again. In my case, the data's hash is both computed on the new() 
(to check for an existing instance in an associative array) and on 
this() (to store it). Also, overloaded forms of new() and this() taking 
a dchar[] both have to converted the data to UTF-8 prior to calling the 
respective char[] variants;

Given this, I think a solution should be found to better encapsulate new().

Suggestion:

- forms of "new(...) Class(...)" are deprecated
- this() has to call the appropriate new() prior to accessing any of its 
members. Otherwise the default class allocator is called.
   - any arguments to new() are be passed to this() instead

E.g.

class Foo
{
     private int attr z;

     private new(uint size, int param)
     {
         ... allocate memory according to param
     }

     this(float bla, int param)
     {
         int x = param * 3;
         new(x);
         z = bla * param;
     }

     this(float bla)
     {
         // default allocator called
         z = cast(int) bla / 2;
     }
}

--
Luís Marques
Dec 29 2006
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Luís Marques wrote:
 Hello all.
 
 I need to build a class for which there should be only one instance with 
 a given attribute (of type char[]).
 
 That can be provided by a class allocator with the following form:
 
     new (uint size, char[] data)
     {
         ...
     }
 
 The same "data" must then also be a constructor parameter:
 
     this(char[] data)
     {
         this.data = data;
         ...
     }

I don't think that's what custom allocators were designed to do. They should only allocate some memory (and register it with the gc if necessary).
 Allocations then take the form:
 
 new("my string") ClassType("my string");
 
 That is problem #1, having to repeat the string. We could wrap this in a 
 static method or in a template, but that takes away the point of a 
 customized new: we are mostly back to the C++ method of having a private 
 constructor and providing a factory method.

Well, in D the factory method can be static opCall(), so allocation can look like this: ClassType("my string") which looks a lot cleaner than the normal ClassType.create("my string").
Dec 29 2006
parent reply =?ISO-8859-1?Q?Lu=EDs_Marques?= <luismarques gmail.com> writes:
Frits van Bommel wrote:
 I don't think that's what custom allocators were designed to do. They 
 should only allocate some memory (and register it with the gc if 
 necessary).

Well, allocators do take parameters. The only reason for that has to be being able to customize how the memory is allocated, right? From what I can see in my example my example still applies (I have seen several people use new for singleton patterns). Perhaps you disagree. Would you care to elaborate?
 Well, in D the factory method can be static opCall(), so allocation can 
 look like this:
     ClassType("my string")
 which looks a lot cleaner than the normal ClassType.create("my string").

Well point out, thanks! But still, isn't new ClassType("my string") better?
Dec 29 2006
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Luís Marques wrote:
 Frits van Bommel wrote:
 I don't think that's what custom allocators were designed to do. They 
 should only allocate some memory (and register it with the gc if 
 necessary).

Well, allocators do take parameters. The only reason for that has to be being able to customize how the memory is allocated, right? From what I can see in my example my example still applies (I have seen several people use new for singleton patterns). Perhaps you disagree. Would you care to elaborate?

Yes they do take parameters, and the reason is indeed to customize how memory is allocated. But unless they throw an exception, they do have to actually _allocate_ some memory. If they don't throw, the return value must be a void* to a newly-allocated piece of memory. So what I gather you're trying to do (potentially return a pointer to an already-existing object) isn't acceptable behavior for a custom allocator.
 Well, in D the factory method can be static opCall(), so allocation 
 can look like this:
     ClassType("my string")
 which looks a lot cleaner than the normal ClassType.create("my string").

Well point out, thanks! But still, isn't new ClassType("my string") better?

A 'new' (that doesn't throw) is supposed to always actually creates a *new* object... Anything that conditionally creates a new object to return should really be a function/method/static opCall, not a custom allocator or constructor. It's just not what they're meant to do.
Dec 29 2006
parent reply =?ISO-8859-1?Q?Lu=EDs_Marques?= <luismarques gmail.com> writes:
Frits van Bommel wrote:
 Yes they do take parameters, and the reason is indeed to customize how 
 memory is allocated. But unless they throw an exception, they do have to 
 actually _allocate_ some memory. If they don't throw, the return value 
 must be a void* to a newly-allocated piece of memory.
 So what I gather you're trying to do (potentially return a pointer to an 
 already-existing object) isn't acceptable behavior for a custom allocator.

You are right. If I return an existing object it will be initialized to default values. I guess that means the solution to a singleton pattern proposed by Burton Radons does not work (http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=D&artnum=14520) Still, it's a pity that "new ClassType()" cannot be used to transparently return an existing object (conditionally or not). -- Luís Marques
Dec 29 2006
next sibling parent reply Thomas Kuehne <thomas-dloop kuehne.cn> writes:
Luís Marques <luismarques gmail.com> schrieb:
 Frits van Bommel wrote:
 Yes they do take parameters, and the reason is indeed to customize how 
 memory is allocated. But unless they throw an exception, they do have to 
 actually _allocate_ some memory. If they don't throw, the return value 
 must be a void* to a newly-allocated piece of memory.
 So what I gather you're trying to do (potentially return a pointer to an 
 already-existing object) isn't acceptable behavior for a custom allocator.

You are right. If I return an existing object it will be initialized to default values. I guess that means the solution to a singleton pattern proposed by Burton Radons does not work (http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=D&artnum=14520) Still, it's a pity that "new ClassType()" cannot be used to transparently return an existing object (conditionally or not).

Where is the problem? # # class Some{ # int dummy; # # this(){ # static Some existing; # if(existing is null){ # existing = this; # }else{ # this = existing; # } # } # } # # import std.stdio; # # int main(){ # Some a = new Some(); # Some b = new Some(); # a.dummy = 13; # writefln("b.dummy: %s", b.dummy); # # return 0; # } # If you use this pattern alot, the GC will have to do some more cleaning. Thomas
Dec 29 2006
next sibling parent =?ISO-8859-1?Q?Lu=EDs_Marques?= <luismarques gmail.com> writes:
Thomas Kuehne wrote:
 # class Some{
 #    int dummy;
 # 
 #    this(){
 #       static Some existing;
 #       if(existing is null){
 #          existing = this;
 #       }else{
 #          this = existing;
 #       }
 #    }
 # }
 # 
 # import std.stdio;
 # 
 # int main(){
 #    Some a = new Some();
 #    Some b = new Some();
 #    a.dummy = 13;
 #    writefln("b.dummy: %s", b.dummy);
 # 
 #    return 0;
 # }
 #
 
 If you use this pattern alot, the GC will have to do some more cleaning.

I did not know that was possible (from my searches on google perhaps several other people didn't too?). About the GC, might this make it better? this() { static Some existing; if(existing is null){ existing = this; }else{ delete this; this = existing; } } The only minor issue remaining is perhaps that some CPU cycles are wasted creating the unnecessary object? Thanks for this information :) -- Luís Marques
Dec 29 2006
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Thomas Kuehne wrote:
 Luís Marques <luismarques gmail.com> schrieb:
 Frits van Bommel wrote:
 Yes they do take parameters, and the reason is indeed to customize how 
 memory is allocated. But unless they throw an exception, they do have to 
 actually _allocate_ some memory. If they don't throw, the return value 
 must be a void* to a newly-allocated piece of memory.
 So what I gather you're trying to do (potentially return a pointer to an 
 already-existing object) isn't acceptable behavior for a custom allocator.

default values. I guess that means the solution to a singleton pattern proposed by Burton Radons does not work (http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=D&artnum=14520) Still, it's a pity that "new ClassType()" cannot be used to transparently return an existing object (conditionally or not).

Where is the problem? # # class Some{ # int dummy; # # this(){ # static Some existing; # if(existing is null){ # existing = this; # }else{ # this = existing; # } # } # } # # import std.stdio; # # int main(){ # Some a = new Some(); # Some b = new Some(); # a.dummy = 13; # writefln("b.dummy: %s", b.dummy); # # return 0; # } # If you use this pattern alot, the GC will have to do some more cleaning. Thomas

Whoa there, 'this' assigning? I feel a disturbance in the Source. I think that is not valid behavior, from an OO point of view. The spec doesn't mention anything like that (AFAIK), and from the implementation point of view, it's undefined behavior: From some small tests I've made you can only change the 'this' pointer as if it was an inout ref, if 'this()' is not called as a parent constructor (that is, as a part of the construction of a subclass). I would say this merits a bug. ---- Code: ---- import std.stdio; import stdext.stdio; class Foo { static bool first = true; this() { if(first){ first = false; writeln("IN FIRST, this= ", cast(void *)this); this = new FooBaz(); writeln("END FIRST, this= ", cast(void *)this); } } } class FooBar : Foo { int[100] iar; this() { writeln("IN FooBar(), this= ", cast(void *)this); } } class FooBaz : Foo { this() { writeln("IN FooBaz(), this= ", cast(void *)this); } } int main(char[][] args) { writeln(">>> NEW Foo:"); Foo foo = new Foo(); writeln("foo= ", cast(void *)foo); Foo.first = true; writeln(">>> NEW FooBar:"); FooBar myfoo = new FooBar(); writeln("#myfoo= ", cast(void *)myfoo); writeln("#myfoo as FooBaz ", cast(FooBaz) myfoo); writeln("#myfoo as FooBar ", cast(FooBar) myfoo); return 0; } -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Dec 30 2006
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Luís Marques wrote:
 Frits van Bommel wrote:
 Yes they do take parameters, and the reason is indeed to customize how 
 memory is allocated. But unless they throw an exception, they do have 
 to actually _allocate_ some memory. If they don't throw, the return 
 value must be a void* to a newly-allocated piece of memory.
 So what I gather you're trying to do (potentially return a pointer to 
 an already-existing object) isn't acceptable behavior for a custom 
 allocator.

You are right. If I return an existing object it will be initialized to default values. I guess that means the solution to a singleton pattern proposed by Burton Radons does not work (http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&g oup=D&artnum=14520) Still, it's a pity that "new ClassType()" cannot be used to transparently return an existing object (conditionally or not). -- Luís Marques

Why should "new ClassType()" be able to return the same instance? What's wrong with ClassType.getInstance() ? Or in your case ClassType.getInstance("my string") ? -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Dec 30 2006
prev sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Luís Marques wrote:
 Hello all.
 
 I need to build a class for which there should be only one instance with 
 a given attribute (of type char[]).

Isn't this the standard idiom for this kind of keyed singleton pattern? class MyClass { private MyClass[char[]] instances; private this(char[] data) { // Do stuff } public static MyClass getInstance() { if (data in instances) { return instances[data]; } else { MyClass obj = new MyClass(data); instances[data] = obj; return obj; } } } I don't see why a custom allocator ever needs to be involved. --benji
Dec 29 2006
parent BCS <nothing pathlink.com> writes:
Benji Smith wrote:
 Luís Marques wrote:
 Hello all.

 I need to build a class for which there should be only one instance 
 with a given attribute (of type char[]).

Isn't this the standard idiom for this kind of keyed singleton pattern?

 
 I don't see why a custom allocator ever needs to be involved.
 
 --benji

I think what would be needed is a total replacement for the creation of an object. This would require that this: new ClassName(agrs); translate to something like this: ClassName.opTotalNewReplacment(args); I think that what Luis is looking for is the means to use a singleton class with the standard new syntax. This would be nice for things like templates. template Foo(T) { class Foo { T[] t; this() { t.length = 1; t[0] = new T; // <<<<< //this template can't be used unless new type // construction is allowed } } } That said, I don't yet have an opinion on if this is a good idea.
Dec 29 2006