March 14, 2005
In article <d1080p$10j6$1@digitaldaemon.com>, Walter says...
>
>Fixed what I broke in 0.117.
>
>http://www.digitalmars.com/d/changelog.html

On Windows that bug about multiple definitions is gone but on Linux I get it.
For example take the same mod1.d mod2.d and mod3.d and
dmd -c mod1.d
etc
dmd mod1.o mod2.o mod3.o
gcc mod1.o mod2.o mod3.o -o mod1 -lphobos -lpthread -lm
mod3.o(.bss+0x0): multiple definition of `_init_4mod15Foo_i3Foo'
mod2.o(.bss+0x0): first defined here
collect2: ld returned 1 exit status

It works ok if I just run "dmd mod1.d mod2.d mod3.d"


March 14, 2005
In article <d12ol1$a5u$1@digitaldaemon.com>, Ben Hinkle says...
>
>
>>Makes sense to me.  Structs in D have been relegated to an "advanced" programming technique anyway, for a multitude of reasons (pass-by-value, pointers, etc).
>
>Why do you say structs are advanced? They are simpler than classes. To me they are a step between basic types like ints and doubles and classes.

I guess it's really a matter of opinion.  D to me seems to cater to classes with more features and a cleaner syntax for reference semantics.  This leaves structs as a sort of "ugly-duckling" by relying on pointers to get the same job done.

>Do you see the compile-time check as disallowing non up-casts? My first assumtion was that casting stays the same - no checks at all - but I can see how the analogy to class casting would be nice, too.
>

Disallowing downcasts (parent to child) is probably the only sane approach as there's no way to guarantee the layout of the struct due to aliasing.  Of course one could always cast through void* via Walter's "static cast idiom", which plays well with everything else.

>
>I think the only danger with adding inheritance-like features to structs is that people would confuse structs with classes. Plus we should figure out a way to avoid slicing structs. Maybe implicit upcasts would have to get the boot.
>

I agree that it would lend to some confusion. The concept would place a burden of education on the would-be programmer first, but not without granting him/her some useful (and optional) features.

Implicit upcasts should be well within reach of this idea:

Given:
> struct Base{}
> struct Child: Base{}
> struct Grandchild: Child{}
> Child a;

Implicit cast followed by value-copy of Base-only members:
> Base b = a;

Implicit cast to pointer type:
> Base* bp = &a;

Its the "lvalue type member copy" feature that would really make this useful IMO.  Everything else is cake.


Since the type information doesn't exist at runtime, you cannot perform implicit downcasts.  Since all you have are either raw pointers or values, the workarounds are unsafe, ugly and ill-advised:

Explicit downcast via static cast idiom (dangerous)
> Child* c = cast(Child*)cast(void*)bp;

Explicit downcast followed by value copy (very dangerous)
> Child d = *(cast(Child*)cast(void*)bp);

- EricAnderton at yahoo
March 14, 2005
>>>Makes sense to me.  Structs in D have been relegated to an "advanced" programming technique anyway, for a multitude of reasons (pass-by-value, pointers, etc).
>>
>>Why do you say structs are advanced? They are simpler than classes. To me they are a step between basic types like ints and doubles and classes.
>
>I guess it's really a matter of opinion.  D to me seems to cater to classes with more features and a cleaner syntax for reference semantics.  This leaves structs as a sort of "ugly-duckling" by relying on pointers to get the same job done.

oh, ok. I guess I'd say structs should be handled by value most of the time. That is, I wouldn't use a struct to get the same job done as a class - I'd use a class. But I agree pointers are more common with large structs.

>>Do you see the compile-time check as disallowing non up-casts? My first assumtion was that casting stays the same - no checks at all - but I can see how the analogy to class casting would be nice, too.
>>
>
>Disallowing downcasts (parent to child) is probably the only sane approach as there's no way to guarantee the layout of the struct due to aliasing.  Of course one could always cast through void* via Walter's "static cast idiom", which plays well with everything else.

I don't know. I can cast int* to double* without a peep from the compiler. So why can't I cast a Foo* to a Bar*? Who cares if it is an up or down cast - it's just a pointer cast.

>Given:
>> struct Base{}
>> struct Child: Base{}
>> struct Grandchild: Child{}
>> Child a;
>
>Implicit cast followed by value-copy of Base-only members:
>> Base b = a;

To me this one is the scary one. Slicing is a common newbie C++ mistake - and given that classes are passed by reference it would look wierd that assigning to Base would be fine if they were classes but can drop data if they were structs. One should have to work to drop data IMO (eg, by casting to the base or accessing a member). It is tempting to add it, though, given that the OP's request was basically sub-classing in order to add member functions.


March 14, 2005
"Carlos Santander B." <csantander619@gmail.com> wrote in message news:d12r1b$bsp$1@digitaldaemon.com...
> Walter wrote:
> > It'll link in if protoerror.obj is specifically specified on the command line. I'll fix it.
>
> This has been a problem with templates for a while, and I hope you address it fully because it's really complex. When having many templates in different modules, the linker complains about undefined symbols and you have to link in the oddest object files so it can go on. Obviously, the problem vanishes when you try to reduce it to send a bug.

The problem happens when the only thing you're linking from a file is a template expansion.


March 14, 2005
In article <d130h8$glu$1@digitaldaemon.com>, Ben Hinkle says...
>>Disallowing downcasts (parent to child) is probably the only sane approach as there's no way to guarantee the layout of the struct due to aliasing.  Of course one could always cast through void* via Walter's "static cast idiom", which plays well with everything else.
>
>I don't know. I can cast int* to double* without a peep from the compiler. So why can't I cast a Foo* to a Bar*? Who cares if it is an up or down cast - it's just a pointer cast.

The ability to cast any scalar pointer to another seems to be a holdover from C/C++ IMO.  If I had to guess, DMD treats int* the same as void*.  Honestly, I feel that one's a bug.  Your example of casting int* to double* exemplifies that.

>
>>Given:
>>> struct Base{}
>>> struct Child: Base{}
>>> struct Grandchild: Child{}
>>> Child a;
>>
>>Implicit cast followed by value-copy of Base-only members:
>>> Base b = a;
>
>To me this one is the scary one. Slicing is a common newbie C++ mistake - and given that classes are passed by reference it would look wierd that assigning to Base would be fine if they were classes but can drop data if they were structs. One should have to work to drop data IMO (eg, by casting to the base or accessing a member). It is tempting to add it, though, given that the OP's request was basically sub-classing in order to add member functions.
>

Your use of the word 'slicing' in this context threw me for a loop the first time you used it; I kept thinking 'array slicing'.   But the thought grew, and suddenly the following was set in my mind:

> Base b = a[Base]; // literally 'slicing' a struct

Which is funky to say the least, but it sort of gets the point across.  At least it's more explicit than my earlier suggestion of an implicit cast.  Here, we're saying "slice 'a' down to 'Base'" instead of "make 'a' look like 'Base'".  Also 'Base' only exists at runtime, so this would play fair with opIndex() and friends.

But after all, its just an idea.  I personally like this one about as much as the casting examples I drafted earlier.


Another side-effect is how overrided base methods are handled since we've done away with vtables.

> Child *c = new Child();
> Base *b = c;
> c.foo(); // calls Child.foo()
> b.foo(); // calls Base.foo()

Its divergent from the assumed class-like behavior since with classes you'd expect c.foo() to be called in both cases.  Is this too subtle?

..just covering all the bases.
- EricAnderton at yahoo
March 14, 2005
Ben Hinkle wrote:

> Presumably the OP wants Rect to be implicitly converted to RECT, too.

That doesn't work if "Rect" adds any new fields, though ?

But as long as Rect.sizeof == RECT.sizeof, just cast away...

> It might
> be fun to explore expanding the role of structs to have some C++-like behaviors
> like constructors and "inheritance" (but no vtables).

IMHO, if you want classes you know where to find them...

Adding methods to structs is something of an ugly hack.

--anders
March 14, 2005
On Mon, 14 Mar 2005 11:25:07 +0100, Anders F Björklund wrote:

> Ben Hinkle wrote:
> 
>> Presumably the OP wants Rect to be implicitly converted to RECT, too.
> 
> That doesn't work if "Rect" adds any new fields, though ?
> 
> But as long as Rect.sizeof == RECT.sizeof, just cast away...
> 
>> It might
>> be fun to explore expanding the role of structs to have some C++-like behaviors
>> like constructors and "inheritance" (but no vtables).
> 
> IMHO, if you want classes you know where to find them...
> 
> Adding methods to structs is something of an ugly hack.
> 

Why? Are you suggesting that there is something fundamentally wrong with entities, which are passed as values and not as references, having methods? I can't see what is so wrong with that concept.

-- 
Derek Parnell
Melbourne, Australia
14/03/2005 10:07:54 PM
March 14, 2005
Derek Parnell wrote:

>>IMHO, if you want classes you know where to find them...
>>Adding methods to structs is something of an ugly hack.
> 
> Why? Are you suggesting that there is something fundamentally wrong with
> entities, which are passed as values and not as references, having methods?
> I can't see what is so wrong with that concept.

No, that's OK... Just that when you start to add inheritance
and constructors and the rest of the class stuff it's "too much"?

Then again, structs and even typedefs having methods do have
some neat uses for extending OS types or validating custom ones.


I just don't like OOP code using struct for "performance reasons".
But that could just be since I'm used to Java, and not to C++... ?

But I would still like my D structs to be more like they are in C,
possibly with some methods or even "extends" sugar sprinkled on top?


A little context and code examples could probably help the discussion.
--anders
March 14, 2005
>Your use of the word 'slicing' in this context threw me for a loop the first time you used it; I kept thinking 'array slicing'.

oops - sorry. 'Slicing' in C++ is when you, for example, pass a derviced class to a function taking a base class and all the derived-specific data of the object is 'sliced' off. People expect that passing a derived class around would preserve the derived data. Hence why pointers to structs are so important in C++.


March 14, 2005
>Then again, structs and even typedefs having methods do have some neat uses for extending OS types or validating custom ones.

A typedef can have methods? How do you do that?

>I just don't like OOP code using struct for "performance reasons". But that could just be since I'm used to Java, and not to C++... ?

Besides performance structs can have a more natural semantics. See example below.

>But I would still like my D structs to be more like they are in C, possibly with some methods or even "extends" sugar sprinkled on top?

That's the idea. They already have methods, though, so that part is done.

>A little context and code examples could probably help the discussion.

Instead of Rect let's take an even smaller class Point. In MinWin I define for each OS an alias NativePoint for the native point type (POINT, XPoint, GdkPoint). Then the MinWin Point is

# struct Point {
#   NativePoint native;
#   int x(){return native.x;}
#   int y(){return native.y;}
#   ... setters...
#   char[] toString() {...}
#   Point opAdd(Point b) {...}
#   ... etc ...
# }
# Point toPoint(NativePoint p) {...}

So that to pass a Point in variable pt to a function that takes a POINT, for example, you would say pt.native. If I had been able to say

# struct Point : NativePoint {
#   functions specific to MinWin...
# }

and have the conversions be implicit and the x() and y() properties inherited it would make for less code for me to type. Also the convertion function toPoint could possibly go. Given that Point is a small struct it isn't a big deal to explicitly type everything out and one could argue that this would be the case for most structs.