Templates: generic "return null;"

Chris wendlec at tcd.ie
Tue Feb 4 03:04:04 PST 2014


On Tuesday, 4 February 2014 at 00:43:54 UTC, TheFlyingFiddle 
wrote:
> On Monday, 3 February 2014 at 10:25:19 UTC, Chris wrote:
>> Is there a way I can make the return type in getAttribute 
>> generic? null does not work with numbers.
>>
>> MyStruct(T) {
>>  T[T] attributes;
>>  // ....
>>  public auto getAttribute(T attr) {
>>      if (!(attr in attributes)) {
>>        return null; // Doesn't work for numbers!
>>      }
>>      return attributes[attr];
>>    }
>> }
>>
>> void main() {
>>  auto myStr = MyStruct!int(0); // Error
>> }
>
> Whenever i am faced with this situation i do one (or more then 
> one) of the following things.
>
> struct MyStruct(T)
> {
>     T[T] attributes;
>
>    //(1) Forward the underlying access method Eg:
>    auto opBinaryRight(string s : "in")(T attrib)
>    {
>       return attrib in attributes;
>    }
>
>    //(2) make a try method.
>    bool tryAttrib(T attrib, out T outAttrib)
>    {
>        auto p = attrib in attributes;
>        if(p) outAttrib = *p;
>        return p !is null;
>    }
>
>
>
>    //(3) Give user option to set default value.
>    T attribOrDefault(T attrib, T default)
>    {
>        auto p = attrib im attributes;
>        return p is null ? default : attrib;
>    }
>
>
>    //(4) Use Nullable!T (I prefer #5 over this one)
>    Nullable!T attribOrNull(T attrib)
>    {
>        Nullable!T result;
>        auto p = attrib ib attributes;
>        if(p) result = *p;
>        return result;
>    }
>
>    //(5) Use a pointer but not forward in operator.
>    T* attribPtr(T attrib)
>    {
>       return attrib in attributes;
>    }
>
>    //(6) Throw exception (I only do this in combination with 
> one of the above)
>    T attribEx(T attrib)
>    {
>      return *enforce!AttribNotFoundEx(attrib in attributes);
>    }
> }

Thanks for this brief outline.

> My personal preference using #2 and #3 in combination. #2 
> covers the basic case "Is this thing avalible?" and #3 covers 
> the case "Give it to me if it is avalible or use this default 
> value" I think it gives a clear image of what your code is 
> doing at the callsite. Only using #2 or #3 limits you in this 
> sence.

Personally I don't like the idea of passing a default value on 
the user side in this particular case. If the attribute has not 
been set, there is a reason, and I don't want to operate with a 
return value of something that has not been set at all.

I introduced a check similar to #2:

bool hasAttribute(T attr) { ... }

Of course, the user has to use if. Experimentally, I introduced

  auto getAttribute(T attr) {
     if (!(attr in attributes)) {
       return T.init;
     }
     return attributes[attr];
   }

to avoid the if statement and just gently move along, if the 
attribute has not been set, which again leads to the problem of 
#3, i.e. potentially operating with a value of something that 
does not exist in the first place.

> For #1, #4 and #5 i personally stay away from them. They force 
> the caller to either use an if or potentially trigger a null 
> pointer derecerence. (Btw what is the benefit of #4? I have 
> never used it since it seems pointless)

#4 is weird, but that's because I don't fully understand the 
concept behind it.

> I very rarly use attribEx. I don't think code shuld just spew 
> exceptions all over the place. They should be reserved for 
> really bad stuff, like bounds checks. One exception i make to 
> this rule is if i'm dealing with ranges. Since the other 
> methods don't lend themselfs for UFCS-chaing.

I agree. Exceptions should be reserved for serious cases or cases 
where you simply cannot predict all cases (reading random input 
from the internet, for example).


More information about the Digitalmars-d-learn mailing list