The new ?? and ??? operators

Reiner Pope some at address.com
Tue Sep 25 05:33:15 PDT 2007


Max Samukha wrote:
> On Tue, 25 Sep 2007 11:39:51 +0300, "Rioshin an'Harthen"
> <rharth75 at hotmail.com> wrote:
> 
>> "Janice Caron" <caron800 at googlemail.com> kirjoitti viestiss? 
>> news:mailman.289.1190665392.16939.digitalmars-d at puremagic.com...
>>> On 9/24/07, Rioshin an'Harthen <rharth75 at hotmail.com> wrote:
>>>> I can't begin to count how many times I've written code like
>>>>
>>>>     long id = data.GetLong("ID") ?? 0;
>>> Please could you explain what that does, as I don't speak C# and can't
>>> make any sense of it. What is the return type of GetLong()? What is
>>> the type of the variable named data?
>> Certainly. :)
>>
>> GetLong, as the name suggests, is used in this example to return a long 
>> (signed 64-bit value) from the database column ID, with the current database 
>> row referenced through the variable data (which, e.g. could be of type 
>> MySqlDataReader). GetLong may, if the column (in the row accessed) doesn't 
>> contain a value, return null, to signify this, since 0 (or any other value) 
>> may be a legal value in the database.
>>
>>>>     long? id = data.GetLong("ID");
>>> What does the question mark after long mean? What is the type of the
>>> variable named id? Is is "long" or "long?" ?
>> The question mark after a type name means the type is nullable: basically, 
>> in addition to its normal range may contain the value null. A nullable type 
>> can be built using a struct or class, and as far as I'm able to tell from 
>> the standard, that's how the C# compiler creates it.
>>
>> If you wish further information on nullable types, I'd recommend reading 
>> section 8.19 of the C# standard, available at
>>
>> http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf 
> 
> 
> A bit OT. Under the hood C# nullable types are structs with an
> additional bool member. ? is just a syntactic sugar for the templated
> System.Nullable struct. Here is a quickly hacked together example of
> how nullable types could be implemented in D using templates.
> 
> struct Nullable(T)
> {
> 	alias typeof(*this) Type;
> 
> 	T value;
> 	bool isNull = true;
> 
> 	Type opAssign(T v)
> 	{
> 		value = v;
> 		isNull = false;
> 		return *this;
> 	}
> 
> 	static Type opCall(T v)
> 	{
> 	    Type ret;
> 	    ret.value = v;
> 	    ret.isNull = false;
> 	    return ret;
> 	}
> 
> 	/*
> 	Hacks to allow null keyword in assignments/comparisons
> 	if you really want it
> 	*/
> 
> 	Type opAssign(void* v)
> 	{
> 	    assert(v == null);
> 	    isNull = true;
> 	    return *this;
> 	}
> 
> 	int opEquals(void* v)
> 	{
> 	    assert(v == null);
> 	    return isNull;
> 	}
> 
> 	T getValueOrDefault()
> 	{
> 	    return isNull ? T.init : value;
> 	}
> 
> 	T ifNull(T v)
> 	{
> 	    return isNull ? v : value;
> 	}
> 
> 	/*
> 	etc.
> 	opImplicitCasts and struct interfaces will be useful here
> 	*/
> }
> 
> class DataReader
> {
>     Nullable!(T) get(T)(char[] field)
>     {
>         Nullable!(T) ret;
>         return ret;
>     }
> }
> 
> void main()
> {
>     auto data = new DataReader;
> 
>     auto id = data.get!(long)("id");
>     assert(id == null);
> 
>     id = 20;
>     assert(id != null);
> 
>     id = null;
> 
>     // a replacement for 'long x = id ?? -1'
>     long x = id.ifNull(-1);
>     assert (x == -1);
> }
> 
> I'm sure DB people already use something like that.

Looks good. And we can overload the bitwise OR operator to make it look 
a little better (in my opinion). We just add

         Nullable!(T) opOr(Nullable!(T) other)
         {
             return isNull ? other : Type(value);
         }

         T opOr(T other)
         {
             return isNull ? other : value;
         }

to your struct, and then as a new main, we get

void main()
{
     auto data = new DataReader;

     auto id = data.get!(long)("id");
     assert(id == null);

     auto id2 = data.get!(long)("id2");
     id2 = 25;

     auto y = id | id2; // y is a Nullable!(T)
     assert(y != null);

     long x = y | -1;
     assert (x == 25);

     id = 20;
     assert(id | id2 | -1 == 20);
}


Voila!

Unfortunately, I couldn't get short-circuiting to work via using lazy on 
parameters types to opOr. This is because | is parsed 
left-associatively, so that doesn't work. I wonder if some 
expression-templatey work could fix this...

     -- Reiner



More information about the Digitalmars-d mailing list