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