Jump to page: 1 2
Thread overview
class allocators should be more encapsulated
Dec 29, 2006
Luís Marques
Dec 29, 2006
Frits van Bommel
Dec 29, 2006
Luís Marques
Dec 29, 2006
Frits van Bommel
Dec 29, 2006
Luís Marques
Dec 29, 2006
Thomas Kuehne
Dec 29, 2006
Luís Marques
Dec 31, 2006
Bruno Medeiros
Dec 31, 2006
Bruno Medeiros
Dec 29, 2006
Benji Smith
Dec 29, 2006
BCS
December 29, 2006
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
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
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
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
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
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
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
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
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
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