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).