On Sunday 14 November 2010 04:14:29 spir wrote: > On Sun, 14 Nov 2010 03:32:18 -0800 > > Jonathan M Davis <jmdavisp...@gmx.com> wrote: > > On Sunday 14 November 2010 03:08:49 spir wrote: > > > Hello, > > > > > > > > > There seems to be 2 main differences between structs & classes: > > > 1. structs instances are direct values, implement value semantics; > > > while class instances are referenced (actually "pointed") 2. classes > > > can be subtyped/subclassed in a simple way; structs cannot be really > > > subtyped -- but there is the "alias this" hack > > > > The main thing to remember between structs and classes is that classes > > are polymorphic. If you want polymorphism, use a class. If you don't use > > a struct. Now, if you want a type which is _always_ a reference, then > > you should probably choose a class, but it's quite easy to have a struct > > which is a reference type by having its member variables are on the heap > > and don't get deep copied be a postblit constructor. It is more > > immediately clear, though, that a class is a reference type. > > > > You can get more nuanced on the reasons why you should pick a struct or a > > class, but polymorphism is really what it generally comes down to. If > > you never intend to subclass it or have it implement interfaces, then it > > should probably be a struct. > > Thank you, Jonathan. > But what about the copy issue. For instance, in a parsing lib, I currently > have a "Source" class that is a kind of "cursored" text (think at a text > file), so it's only: > > class Source { > string text; > uint index; > this (string text, uint index) { > this.text = text; > this.index = index; > } > } > > The advantages that match functions return a source that has stepped > forward after successful match (hope you see what I mean, it's a common > issue in parsing). Conceptually, it's pure data, meaning it should be a > struct. Also, as you point above, it requires no polymorphism. But I made > it a class, because: * It's created only once, so heavier creation on the > heap is irrelevant. * It's passed from match method to match method huge > numbers of time (can be millions of times when parsing code). If I had a > struct, it would be copied! (Even if the text itself can be prevented from > copy by putting it in a plain array, or "pointed", the fields are still > copied.) * Higher-level patterns that delegate matching to sub-patterns > directly get updated source (with new index), precisely because the source > is referenced. > > For instance, the actual match method of the Choice pattern type holds: > // ... > foreach (Pattern pattern ; this.patterns) { > try > return pattern.check(source); // ******* > catch (MatchError e) { > // ... > } > } > // ... > } > > The marked line works because Source is referenced. Else, I would have to > update source.index manually. Similarly, for a Composition pattern type > (every sub-pattern must match in sequence): > > foreach (Pattern pattern ; patterns) { > result = pattern.check(source); > nodes ~= result.node; > } > > The source is automagically updated by each sub-pattern match. > > Thus, this a case where semantic (of value) and feature (no polymorphism) > rationales seem both to conflict with practical programming reasons.
Well, like I said, it can be more nuanced. Polymorphism is the main thing, but other factors can matter. In this case, you have an efficiency problem in using a struct. The are several ways around that: 1. Make the struct a reference type, making its internals pointers or references. 2. Make the struct a value type, but make its internals pointers are references and use COW (copy-on-write). 3. Use a pointer to a struct. 4. Use a class. Personally, I think that #1 can be useful, but generally that's probably not what you want. #2 doesn't work correctly right now due to bugs regarding destructors, and it's a pretty complicated solution anyway. As for #3 and #4, if you're not going to use polymorphism, then there really isn't much difference between using pointers to structs and making the type a class - the main difference between a pointer and a reference being that a reference is polymorphic (if you had a pointer to a class, it wouldn't be polymorphic), so it's arguably better to use pointer to a struct than a class when it doesn't need to be polymorphic, but I suppose that it's a matter of taste. If you make it a struct, you at least _can_ stick it on the stack if you want though. It also makes them easier to copy, since you don't have to declare a clone() function or something similar. However, making it a class makes it obvious that it's intended to be used as a reference type rather than a value type, so it all depends on what you're trying to do. - Jonathan M Davis