View mode: basic / threaded / horizontal-split · Log in · Help
December 29, 2006
class allocators should be more encapsulated
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
December 29, 2006
Re: class allocators should be more encapsulated
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").
December 29, 2006
Re: class allocators should be more encapsulated
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?
December 29, 2006
Re: class allocators should be more encapsulated
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.
December 29, 2006
Re: class allocators should be more encapsulated
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
December 29, 2006
Re: class allocators should be more encapsulated
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
December 29, 2006
Re: class allocators should be more encapsulated
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
December 29, 2006
Re: class allocators should be more encapsulated
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
December 29, 2006
Re: class allocators should be more encapsulated
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.
December 31, 2006
Re: class allocators should be more encapsulated
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.
>> 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
> 

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
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home