New set class
Michiel
nomail at please.com
Mon Feb 26 02:15:58 PST 2007
Bill Baxter wrote:
>>> If you add an opApply with the signature
>>> int opApply(int delegate(uint i, inout Type) dg) { ... }
>>>
>>> it will allow this kind of foreach too:
>>> foreach(i, e; myset) {
>>> ...
>>> }
>>
>> That's why I didn't do it. What meaning would it have for a set, since
>> they have no index?
>
> It's useful to the user who wants to print out the items numbered or
> something. Or they have a set of 10 items and they want to copy them to
> a preallocated 10-item list. Or they want to make another associative
> array that maps numbers to the items in the set. Or they want to do
> something with only the first five items in the set then if(i>5) break;.
> Or they want to append a number to every item in the set. Or they want
> to split the set into two smaller sets arbitrarily using if (i%2).
>
> Yes the user can always declare their own damn counter, but it doesn't
> make your code much more complex, and it does make their lives a little
> bit easier.
I feel that if I added that syntax, it would imply that the counter has
some sort of relevance to the set, which it really doesn't.
A counter that increments by one each iteration has real meaning for an
array. So does a key-type for an associative array. But for a set...
sorry, I'd just feel dirty adding it.
>>> Also I would just leave out opApplyReverse. That way using it is a
>>> compile-time error. Or if you're dead set on it, at least have it throw
>>> an exception. I believe assert() is a no-op in release builds, meaning
>>> your program will fail in strange ways in release. Actually I'm
>>> surprised that compiles without a return statement.
>>
>> assert(0) is evaluated by D as a point in the code you can't ever reach.
>> I got the idea from the opCmp example:
>>
>
> Interesting. I didn't realize that. That's really not obvious from the
> docs that that's what happens:
>
> """
> The expression assert(0) is a special case; it signifies that it is
> unreachable code. Either AssertError is thrown at runtime if it is
> reachable, or the execution is halted (on the x86 processor, a HLT
> instruction can be used to halt execution). The optimization and code
> generation phases of compilation may assume that it is unreachable code.
> """ -- http://www.digitalmars.com/d/expression.html#AssertExpression
>
> Nowhere does it say it that it is "unconditional regardless of
> debug/release flags".
>
> This page talks about asserts but doesn't mention debug/release behavior
> either.
> http://www.digitalmars.com/d/dbc.html
>
> Actually I can't find where disabling asserts for release builds is
> documented, though I'm sure I've seen it somewhere before. A quick
> test, however, reveals that you are indeed correct: "regular" asserts
> turn off in a release build, but assert(0) does not.
>
> Well that's good to know. Still, it generates a runtime error, when you
> really would prefer a compile-time error. So I think it's a bad idea
> to start putting in lots of
> opFoo() { assert(0,"This method doesn't exist"); }
> Once you start putting those in, where do you stop? I'm sure there are
> other operators and functions that Set does not implement.
>
> It's almost like comments along the lines of:
> // this is an integer that does the counting:
> int counter;
>
> The fact that the method is not there is all the documentation you need.
>
> But if you really want to do put such things in your code you could try
> this:
> struct Set(T) {
> ...
> void opApplyReverse()() {static assert(0,"don't be silly");}
> }
>
> Since foo is a template member, no attempt is made to compile it unless
> it's actually called from somewhere. If you do try to call it then you
> trip the static assert at compile-time.
No, if you use a static assert, the compilation will fail as soon as you
instantiate the class somewhere. Not if you try to use foreach_reverse.
If it weren't a template class, the compiler would throw the error in
any case.
You're right that a compiler error would be preferable to a runtime
error, but simply leaving the function out doesn't give as clear an
error message ("Error: cannot infer type for e"). It would be ideal if I
could have both.
But well, I don't understand why unittests are done at runtime either.
>>> Finally
>>> Set!(T[0]) set(T...)(T elements)
>>> why not just
>>> Set!(T) set(T)(T elements...) ??
>>
>> You only use that syntax if T is an aggregate type. So the resulting set
>> would actually be Set!(T[0]) anyway. I think I tried this before and had
>> some problems, but I'm not certain now.
>
> I don't follow what you mean about "aggregate type". The unittest uses
> int as the type. I don't think 'int' is considered an aggregate.
Well, That's what the 'void func(T name...)' syntax is for. If T is an
aggregate type (array, class, struct, etc.) you can either supply that
type to the func, or all of its members separated by comma's.
I suspect that you meant this one: 'void func(T name, ...)', which is
something different. I haven't tried it, and I will.
> Another thing you might want to add to your set class is opCat (~) and
> opCatAssign (~=). Those are pretty commonly used in D for appending
> something to something else. They'd just be synonyms for add and union.
Good idea. I'll add them.
--
Michiel
More information about the Digitalmars-d
mailing list