Thread overview
Idea to ease tail-const class reference woes
Mar 21, 2008
Janice Caron
Mar 21, 2008
Janice Caron
Mar 21, 2008
Janice Caron
Mar 21, 2008
Janice Caron
Mar 22, 2008
Janice Caron
Mar 23, 2008
Simen Kjaeraas
March 21, 2008
I thought of this idea to compromise a bit in the tail-const class reference debate.

Before I outline my idea, let me just say that my first preference is to be able to have true tail-const class references, or at least to have Walter commit to providing tail-const class references before D2.0 is finalized. But it's looking more and more like that isn't going to happen (not for any good reason I can see BTW).

So here goes...

We have tail-const value types without a problem in D, they look like:

const(int)* tailConstInt;

tailConstInt can be rebinded, but you cannot change what it points to.  It is easy to instantiate or assign a tail-const int:

int x = 5;
const(int)* cx = &x;

Or if you need a tail-const int that lives on the heap:

int *x = new int; // would be nice to be able to do new int(5)
*x = 5;
const(int)* cx = x;

If you need a tail-const class reference, you can do

C myClass = new C;
const(C)* constClass = &myClass;

Now, I can rebind constClass, but I cannot change the class through that reference.  However, once this function goes out of scope, if constClass is a global, I now have to keep the stack frame around.  If I want to allocate a class reference on the heap, there is no direct way to do it.  The easiest way to do it I know of is:

C myClass = new C;
const(C)[] tmp;
tmp ~= myClass;
const(C)* constClass = &tmp[0];

Which not only is a pain (and ugly), but I now have allocated an extra chunk of memory to hold a class reference.  I do not have to do this with an int, because the int is not inherently a reference, and I don't need two levels of indirection.

So my idea is to include in the class data a const reference to itself. Then provide a way to access that const reference.  In this way, tail-const classes become easy:

C myClass = new C;
const(C)* constClass = &myClass.constref;

Now, no extra heap allocation, and it is easy to specify a tail-const class reference.  Of course, there is still the issue of double-dereferencing to use the class, but that's an O(1) operation, and so shouldn't matter too much.  True tail-const class references would be preferrable for this reason, but this is a compromise.

Some would argue that doing this adds extra heap data, but in reality, allocations happen at the smallest in 16 or 32 byte chunks.  Chances are high that your class heap block still has 4 bytes (or 8 for 64-bit archs) left to put the extra pointer in.  Only in specific cases would this become a burden.

Does this sound reasonable?

-Steve


March 21, 2008
On 21/03/2008, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>  So my idea is to include in the class data a const reference to itself.
>  Then provide a way to access that const reference.  In this way, tail-const
>  classes become easy:
>
>  C myClass = new C;
>  const(C)* constClass = &myClass.constref;
>
>  Does this sound reasonable?

Sounds more work than

    import std.typecons;
    Rebindable!(C) myClass = new myClass;

which is planned for the future. (See D2.012 change log)
March 21, 2008
Oops. Typo. Let's try that again...

Sounds more work than

    import std.typecons;
    Rebindable!(C) myClass = new C;

which is planned for the future. (See D2.012 change log)
March 21, 2008
"Janice Caron" wrote
> Oops. Typo. Let's try that again...
>
> Sounds more work than
>
>    import std.typecons;
>    Rebindable!(C) myClass = new C;
>
> which is planned for the future. (See D2.012 change log)

Cool, I didn't see that (understandably :) )

One thing that does sort of make me nervous is the added bloat to the type system for each type that I want to create a const class ref.  I count 3 functions, which looks like there will be more when opImplicitCast is introduced, and the added TypeInfo.

We'll see how much it makes a difference when the implementation is finished.

Of course, I still prefer builtin tail-const class references, as no extra code bloat is necessary, no extra function calls (that do nothing but cast) are necessary, and everything just works in the most efficient manner possible.  But this is a workable solution.

Also, you're example should be:

Rebindable!(const(C)) myClass = new C;

-Steve


March 21, 2008
On 21/03/2008, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>  Also, you're example should be:
>
>  Rebindable!(const(C)) myClass = new C;

Ah yes. You're probably right.

Well, we'll just have to wait and see how it pans out, once it's added to the language for real.
March 21, 2008
On 21/03/2008, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>  Also, you're example should be:
>
>  Rebindable!(const(C)) myClass = new C;

Ah yes. You're probably right.

Well, we'll just have to wait and see how it pans out, once it's added to the language for real.
March 21, 2008
"Janice Caron" wrote
> Oops. Typo. Let's try that again...
>
> Sounds more work than
>
>    import std.typecons;
>    Rebindable!(C) myClass = new C;
>
> which is planned for the future. (See D2.012 change log)

One further question on this...

If I have:

class C
{
   void foo() {};
}

Rebindable!(const(C)) myC = new C;

myC.foo();

Is the plan for this to compile (I know it doesn't today)? If not, then this is not an acceptable solution.

-Steve


March 22, 2008
On 21/03/2008, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>  Is the plan for this to compile (I know it doesn't today)?

I'm afraid I have no idea what "the plan" is. As we know, this still an undocumented template, there only as a placeholder for a future feature. I guess only Andrei can answer that question, but I would certainly imagine so.
March 23, 2008
On Sat, 22 Mar 2008 00:08:55 +0100, Steven Schveighoffer <schveiguy@yahoo.com> wrote:

> "Janice Caron" wrote
>> Oops. Typo. Let's try that again...
>>
>> Sounds more work than
>>
>>    import std.typecons;
>>    Rebindable!(C) myClass = new C;
>>
>> which is planned for the future. (See D2.012 change log)
>
> One further question on this...
>
> If I have:
>
> class C
> {
>    void foo() {};
> }
>
> Rebindable!(const(C)) myC = new C;
>
> myC.foo();
>
> Is the plan for this to compile (I know it doesn't today)? If not, then this
> is not an acceptable solution.
>
> -Steve

Why should that compile? It clearly says that myC is const, and hence non-const methods should not be callable on it.
Had it been:

class C
{
    const void constFunc() {}
    void nonConstFunc() {}
}

Rebindable!(const(C)) myC = new C;

myC.constFunc(); // should work, both myC and constFunc are const.
myC.nonConstFunc(); // should not work, might change myC's internal state.


--Simen
March 24, 2008
My apologies, you are correct :)  I meant to say what you said.  My point was that with the current template, you cannot do this, because Rebindable!(C) becomes a struct with only a small number of member functions, which does not pass method calls to C.  If there is a plan for this to be fixed, then I will accept this as a less than ideal, but functional solution.

-Steve

"Simen Kjaeraas" wrote
On Sat, 22 Mar 2008 00:08:55 +0100, Steven Schveighoffer  wrote:
> "Janice Caron" wrote
>> Oops. Typo. Let's try that again...
>>
>> Sounds more work than
>>
>>    import std.typecons;
>>    Rebindable!(C) myClass = new C;
>>
>> which is planned for the future. (See D2.012 change log)
>
> One further question on this...
>
> If I have:
>
> class C
> {
>    void foo() {};
> }
>
> Rebindable!(const(C)) myC = new C;
>
> myC.foo();
>
> Is the plan for this to compile (I know it doesn't today)? If not, then
> this
> is not an acceptable solution.
>
> -Steve

Why should that compile? It clearly says that myC is const, and hence
non-const methods should not be callable on it.
Had it been:

class C
{
     const void constFunc() {}
     void nonConstFunc() {}
}

Rebindable!(const(C)) myC = new C;

myC.constFunc(); // should work, both myC and constFunc are const.
myC.nonConstFunc(); // should not work, might change myC's internal state.


--Simen