Jump to page: 1 2
Thread overview
Express "Class argument may not be null" ?
Aug 08, 2017
Johan Engelen
Aug 08, 2017
ag0aep6g
Aug 08, 2017
Johan Engelen
Aug 09, 2017
Johan
Aug 08, 2017
Andre Kostur
Aug 08, 2017
Johan Engelen
Aug 08, 2017
Adam D. Ruppe
August 08, 2017
Hi all,
  How would you express the function interface intent that a reference to a class may not be null?
For a function "void foo(Klass)", calling "foo(null)" is valid. How do I express that that is invalid? (let's leave erroring with a compile error aside for now)

Something equivalent to C++'s pass by reference: "void foo(Klass&)".

(note: I mean D classes, for structs "ref" works)

Thanks,
  Johan

August 08, 2017
On 08/08/2017 08:34 PM, Johan Engelen wrote:
>    How would you express the function interface intent that a reference to a class may not be null?
> For a function "void foo(Klass)", calling "foo(null)" is valid. How do I express that that is invalid? (let's leave erroring with a compile error aside for now)
> 
> Something equivalent to C++'s pass by reference: "void foo(Klass&)".

A contract might be the best you can do:

----
void foo(Klass k)
in { assert(k !is null); }
body {}
----

Or throw an exception.

> (note: I mean D classes, for structs "ref" works)

But you can pass null in a ref parameter:

----
void f(ref int x) @safe {}
void main() @safe
{
    int* p = null;
    f(*p);
}
----
August 08, 2017
On 8/8/17 2:34 PM, Johan Engelen wrote:
> Hi all,
>    How would you express the function interface intent that a reference to a class may not be null?
> For a function "void foo(Klass)", calling "foo(null)" is valid. How do I express that that is invalid? (let's leave erroring with a compile error aside for now)

There isn't a way to do this in the type itself.

One can always create a null class instance via:

MyObj obj;

There is no way to disallow this somehow in the definition of MyObj. With structs, you can @disable this(), and it's still possible but harder to do so.

I would say, however, that if you wanted to express the *intent*, even without a compile-time error, you could use a contract:

void foo(Klass k) in {assert(k !is null);};

Since the contract is part of the signature, this should be symantically what you want.

However, this has to be done on every function that would accept a Klass, there's no way to bake it into the type itself.

-Steve
August 08, 2017
On 8/8/17 2:56 PM, ag0aep6g wrote:
> On 08/08/2017 08:34 PM, Johan Engelen wrote:
>>    How would you express the function interface intent that a reference to a class may not be null?
>> For a function "void foo(Klass)", calling "foo(null)" is valid. How do I express that that is invalid? (let's leave erroring with a compile error aside for now)
>>
>> Something equivalent to C++'s pass by reference: "void foo(Klass&)".
[snip]
> 
> But you can pass null in a ref parameter:
> 
> ----
> void f(ref int x) @safe {}
> void main() @safe
> {
>      int* p = null;
>      f(*p);
> }
> ----

Note that C++ also can do this, so I'm not sure the & is accomplishing the correct goal:

void foo(Klass&);

int main()
{
   Klass *k = NULL;
   foo(*k);
}

However, the in contract does actually enforce the requirement.

-Steve
August 08, 2017
On Tuesday, 8 August 2017 at 18:57:48 UTC, Steven Schveighoffer wrote:
> On 8/8/17 2:34 PM, Johan Engelen wrote:
>> Hi all,
>>    How would you express the function interface intent that a reference to a class may not be null?
>> For a function "void foo(Klass)", calling "foo(null)" is valid. How do I express that that is invalid? (let's leave erroring with a compile error aside for now)
>
> There isn't a way to do this in the type itself.
>
> One can always create a null class instance via:
>
> MyObj obj;
>
> There is no way to disallow this somehow in the definition of MyObj. With structs, you can @disable this(), and it's still possible but harder to do so.

Ok thanks, so this could be a reason for not being allowed to express the non-null-ness.
(I still haven't found peace with the absence of an explicit * for classes)

> I would say, however, that if you wanted to express the *intent*, even without a compile-time error, you could use a contract:
>
> void foo(Klass k) in {assert(k !is null);};

Thanks. I regret leaving compile-time errors out, because in that case adding it to the function documentation would suffice.

(Btw: "Error: function foo in and out contracts require function body". But who uses .di files anyway. ;-)

Cheers,
  Johan

August 08, 2017
On Tuesday, 8 August 2017 at 19:38:19 UTC, Steven Schveighoffer wrote:
>
> Note that C++ also can do this, so I'm not sure the & is accomplishing the correct goal:
>
> void foo(Klass&);
>
> int main()
> {
>    Klass *k = NULL;
>    foo(*k);
> }

In C++, it is clear that the _caller_ is doing the dereferencing, and the dereference is also explicit.

> However, the in contract does actually enforce the requirement.

And adds null pointer checks even when clearly not needed.

- Johan



August 08, 2017
On 2017-08-08 12:38 PM, Steven Schveighoffer wrote:
> On 8/8/17 2:56 PM, ag0aep6g wrote:
>> On 08/08/2017 08:34 PM, Johan Engelen wrote:
>>>    How would you express the function interface intent that a reference to a class may not be null?
>>> For a function "void foo(Klass)", calling "foo(null)" is valid. How do I express that that is invalid? (let's leave erroring with a compile error aside for now)
>>>
>>> Something equivalent to C++'s pass by reference: "void foo(Klass&)".
> [snip]
>>
>> But you can pass null in a ref parameter:
>>
>> ----
>> void f(ref int x) @safe {}
>> void main() @safe
>> {
>>      int* p = null;
>>      f(*p);
>> }
>> ----
> 
> Note that C++ also can do this, so I'm not sure the & is accomplishing the correct goal:
> 
> void foo(Klass&);
> 
> int main()
> {
>     Klass *k = NULL;
>     foo(*k);
> }
> 
> However, the in contract does actually enforce the requirement.

To be fair: it cannot be done in C++ without first invoking Undefined Behaviour (such as dereferencing a nullptr).
August 08, 2017
I was about to say "use NotNull" but there still isn't one in std.typecons. ugh.

But it is just a wrapper type that checks null in the contracts too, so you can do it at the function itself.
August 09, 2017
On 8/8/17 3:59 PM, Johan Engelen wrote:
> On Tuesday, 8 August 2017 at 19:38:19 UTC, Steven Schveighoffer wrote:
>>
>> Note that C++ also can do this, so I'm not sure the & is accomplishing the correct goal:
>>
>> void foo(Klass&);
>>
>> int main()
>> {
>>    Klass *k = NULL;
>>    foo(*k);
>> }
> 
> In C++, it is clear that the _caller_ is doing the dereferencing, and the dereference is also explicit.

In fact it's not doing any dereferencing. It's just under the hood passing a pointer.

>> However, the in contract does actually enforce the requirement.
> 
> And adds null pointer checks even when clearly not needed.

Clearly not needed? I thought the point was to ensure the reference is not null?

-Steve
August 09, 2017
On 8/8/17 4:00 PM, Andre Kostur wrote:
> On 2017-08-08 12:38 PM, Steven Schveighoffer wrote:
>> On 8/8/17 2:56 PM, ag0aep6g wrote:
>>> On 08/08/2017 08:34 PM, Johan Engelen wrote:
>>>>    How would you express the function interface intent that a reference to a class may not be null?
>>>> For a function "void foo(Klass)", calling "foo(null)" is valid. How do I express that that is invalid? (let's leave erroring with a compile error aside for now)
>>>>
>>>> Something equivalent to C++'s pass by reference: "void foo(Klass&)".
>> [snip]
>>>
>>> But you can pass null in a ref parameter:
>>>
>>> ----
>>> void f(ref int x) @safe {}
>>> void main() @safe
>>> {
>>>      int* p = null;
>>>      f(*p);
>>> }
>>> ----
>>
>> Note that C++ also can do this, so I'm not sure the & is accomplishing the correct goal:
>>
>> void foo(Klass&);
>>
>> int main()
>> {
>>     Klass *k = NULL;
>>     foo(*k);
>> }
>>
>> However, the in contract does actually enforce the requirement.
> 
> To be fair: it cannot be done in C++ without first invoking Undefined Behaviour (such as dereferencing a nullptr).

If your "insurance" that null pointers aren't passed is the threat of undefined behavior, then it leaves a lot to be desired.

It's possible, and does happen. The "just don't write bugs" approach doesn't scale.

-Steve
« First   ‹ Prev
1 2