January 31, 2013
On Thursday, 31 January 2013 at 00:15:36 UTC, Zach the Mystic wrote:
>> The problem of using empty struct variables is that they take up memory. They have to, because you can make a pointer to a variable and then you can dereference that variable. There has to be at least a byte of memory to dereference.
>>
>> So, really, the only zero-overhead way to do this is to introduce a new keyword that creates something that you can't take the address of, because it kind of doesn't exist (like a namespace). It exists only in the sense that it can be used to tell the compiler which operators and functions to call. That's what my namespace_thingy is.
>
> I disagree. The compiler can easily tell if a struct is defined with no data, and simply optimize away the pointer in the process.

Here's Bjarne Stroustrup's reasoning for this design choice
http://www.stroustrup.com/bs_faq2.html#sizeof-empty

Q: Why is the size of an empty class not zero?

A: To ensure that the addresses of two different objects will be different. For the same reason, "new" always returns pointers to distinct objects. Consider:

class Empty { };

void f()
{
    Empty a, b;
    if (&a == &b) cout << "impossible: report error
                           to compiler supplier";

    Empty* p1 = new Empty;
    Empty* p2 = new Empty;
    if (p1 == p2) cout << "impossible: report error
                           to compiler supplier";
}

There is an interesting rule that says that an empty base class need not be represented by a separate byte:

struct X : Empty {
    int a;
    // ...
};

void f(X* p)
{
    void* p1 = p;
    void* p2 = &p->a;
    if (p1 == p2) cout << "nice: good optimizer";
}

This optimization is safe and can be most useful. It allows a programmer to use empty classes to represent very simple concepts without overhead. Some current compilers provide this "empty base class optimization".
January 31, 2013
I think my former unholy union of namespace and variable is just too confusing to reason about, so here's my new way of seeing effectively the same thing, but with simpler semantics:

Keyword property is like the keyword struct, except:

1) properties can't have data members
2) properties can't have static member functions
3) properties can't have constructors/destructors
4) property instances always point to null
5) property instances' size is 0
6) if a property is declared inside a class/struct, all its
   member functions receive an implicit 'outer' variable
   which references the encapsulating object. Whether or
   not 'outer' is const ref or ref depends only on the
   constness of the encapsulating object.

Example:

struct T
{
    private int value;

    property PropType
    {
        @property int getIt() const
        {
            return outer.value;
        }

        alias this = getIt;

        PropType opAssign(int v) const
        {
            outer.value = v;
            return this;
        }

        PropType opAssign(string s : "+")(int v) const
        {
            outer.value += v;
            return this;
        }

        bool isCorrect() const
        {
            return outer.value == 42;
        }
    }

    PropType myProp;
}

Usage:

T t;
int v = t.myProp; // lowers to t.myProp.getIt due to alias this
t.myProp = 41;
t.myProp += 1;
assert(t.myProp.isCorrect());
assert(&t.myProp == null);
assert(T.PropType.sizeof == 0);
January 31, 2013
On Thursday, 31 January 2013 at 02:10:51 UTC, TommiT wrote:
> Keyword property is like the keyword struct, except:
>
> 1) properties can't have data members
> 2) properties can't have static member functions
> 3) properties can't have constructors/destructors
> 4) property instances always point to null
> 5) property instances' size is 0
> 6) if a property is declared inside a class/struct, all its
>    member functions receive an implicit 'outer' variable
>    which references the encapsulating object. Whether or
>    not 'outer' is const ref or ref depends only on the
>    constness of the encapsulating object.
(CONT'D)
     Instances of such nested properties can be default-
     constructed only in the lexical scope where the
     property type is defined.

January 31, 2013
On Wednesday, 30 January 2013 at 18:36:17 UTC, Dmitry Olshansky wrote:
[...]
> I have one key problem - the hidden pointer detail.
> In other words how should it find the instance of the outer struct to to access it?
>
> struct A{
> 	int a;
> 	struct B{
> 		void foo(){ a = 42; }
> 	}	
> 	B b;
> }
>
> A a;
> a.b.foo(); //how that b is supposed to know it's outer struct without the hidden pointer?
>
> auto x = a.b;
> x.foo();// and now what?
>
>

Good point.

A property-struct could behave like a struct and also like a regular member function.

Member functions work because they take in a pointer to the struct or class when called, eg

a.b.foo();

becomes

a.b.foo(&a);

auto x = a.b; // returns property value of b, not b itself.
auto x = &a.b; // returns delegate pointer to b
x.foo(); // OK

--rt

January 31, 2013
On Thursday, 31 January 2013 at 01:14:36 UTC, TommiT wrote:
> Here's Bjarne Stroustrup's reasoning for this design choice
> http://www.stroustrup.com/bs_faq2.html#sizeof-empty
>
> Q: Why is the size of an empty class not zero?
>
> A: To ensure that the addresses of two different objects will be different. For the same reason, "new" always returns pointers to distinct objects. Consider:
>
> class Empty { };
>
> void f()
> {
>     Empty a, b;
>     if (&a == &b) cout << "impossible: report error
>                            to compiler supplier";
>
>     Empty* p1 = new Empty;
>     Empty* p2 = new Empty;
>     if (p1 == p2) cout << "impossible: report error
>                            to compiler supplier";
> }
>
> There is an interesting rule that says that an empty base class need not be represented by a separate byte:
>
> struct X : Empty {
>     int a;
>     // ...
> };
>
> void f(X* p)
> {
>     void* p1 = p;
>     void* p2 = &p->a;
>     if (p1 == p2) cout << "nice: good optimizer";
> }
>
> This optimization is safe and can be most useful. It allows a programmer to use empty classes to represent very simple concepts without overhead. Some current compilers provide this "empty base class optimization".

Now classes are a different kettle of fish. I haven't thought them out and I don't think I need to. They may work seamlessly with my idea or be fraught with problems, I don't know.

But there will never be a need for a new empty struct. The operator new makes no sense. There's no data! Imagine an empty struct as a list of functions under a namespace. That's all it really is. Except it just so happens that this namespace has a bunch of built-in functions which allow it to appear in normal code as if it's a *type*. Want it to appear with parens, so it looks like a function you're calling? Just define opCall inside the struct. Want it to appear before an equals sign. Just define opAssign. As an array? opIndex. A basic type such as an int? opGet. There will never be any need to create an instance. You only need one. At runtime, there is no evidence of a pointer at all, just normal data being passed to normal functions.
January 31, 2013
On Wednesday, 30 January 2013 at 21:41:58 UTC, Zach the Mystic wrote:
> On Wednesday, 30 January 2013 at 18:36:17 UTC, Dmitry Olshansky wrote:
>> I have one key problem - the hidden pointer detail.
>> In other words how should it find the instance of the outer struct to to access it?
>>
>> struct A{
>> 	int a;
>> 	struct B{
>> 		void foo(){ a = 42; }
>> 	}	
>> 	B b;
>> }
>>
>> A a;
>> a.b.foo(); //how that b is supposed to know it's outer struct without the hidden pointer?
>>
>> auto x = a.b;
>> x.foo();// and now what?
>
> It seems struct B does need a pointer... and yet it only needs it in compile time. "auto x = a.b" is no different from "alias a.b x", because when you have no data, you have no data to worry about! a.b is nothing but a namespace with full struct semantics.
>
> I can only say that my intuition tells me that this is easily managed. I cannot tell you what adjustments need to be made to the compiler to get this to come out right.
>
> But what if B actually had some data? The only solution is to have one pointer for every struct it's nested inside of. I can imagine it getting tricky in this case. If it were so tricky as to be prohibitive to implement, then all is not lost. You can still implement zero-data structs as properties. In that case, I suggest weaving the implementation in directly with the Highlanders, because Highlanders will be much less appealing for any other use.

What I meant with regards to weaving the implementation with the Highlanders is that if empty structs have access to the outer nest then they are fundamentally different, and being so, will require a syntax to distinguish them. I don't readily have another easy syntax, so they would be forced to monopolize the Highlander syntax as punishment for being so inflexible.
January 31, 2013
On 01/31/2013 03:10 AM, TommiT wrote:
> I think my former unholy union of namespace and variable is just too
> confusing to reason about, so here's my new way of seeing effectively
> the same thing, but with simpler semantics:
>
> Keyword property is like the keyword struct, except:
>
> 1) properties can't have data members
> 2) properties can't have static member functions
> 3) properties can't have constructors/destructors
> 4) property instances always point to null
> 5) property instances' size is 0
> 6) if a property is declared inside a class/struct, all its
>     member functions receive an implicit 'outer' variable
>     which references the encapsulating object. Whether or
>     not 'outer' is const ref or ref depends only on the
>     constness of the encapsulating object.
>
> Example:
>...
> Usage:
>
> T t;
> int v = t.myProp; // lowers to t.myProp.getIt due to alias this
> t.myProp = 41;
> t.myProp += 1;
> assert(t.myProp.isCorrect());
> assert(&t.myProp == null);
> assert(T.PropType.sizeof == 0);

auto v = t.myProp; // ?
January 31, 2013
On Thursday, 31 January 2013 at 03:35:32 UTC, Zach the Mystic wrote:
> Now classes are a different kettle of fish.

Sorry for not being more explicit. Bjarne Stroustrup is the creator of C++, so the Q&A I posted refers to C++, not D. But, long story short, all of the Q&A is applicaple to D, as long as you read every instance of the word 'class' as 'struct'.

Empty structs taking memory is just a fundamental language design thing, it's not something we can discuss, agree/disagree, or change.
January 31, 2013
On Thursday, 31 January 2013 at 09:39:06 UTC, Timon Gehr wrote:
> auto v = t.myProp; // ?

I think it might be fine to allow copy-constructing 'properties', passing them to functions by value or by reference, and such. So, you could do:

void func(ref T.PropType v);

PropType v = t.myProp;
func(v);

But default-construction of 'properties' can be allowed only nested inside the enclosing object (to ensure that the enclosing object exists, and can be passed implicitly as that 'outer' variable to PropType's methods).

Although, I don't see much real value in allowing the kind of code above. So, it might be just as well to just disallow copying of 'properties' except when it happens as a part of copying the enclosing object.
January 31, 2013
On Thursday, 31 January 2013 at 12:44:18 UTC, TommiT wrote:
> On Thursday, 31 January 2013 at 03:35:32 UTC, Zach the Mystic wrote:
>> Now classes are a different kettle of fish.
>
> Sorry for not being more explicit. Bjarne Stroustrup is the creator of C++, so the Q&A I posted refers to C++, not D. But, long story short, all of the Q&A is applicaple to D, as long as you read every instance of the word 'class' as 'struct'.
>
> Empty structs taking memory is just a fundamental language design thing, it's not something we can discuss, agree/disagree, or change.

I hope this isn't the final word on the topic.