April 05, 2005
>
> Some things are more likely to fail than others. When calling new to allocate a new object fails it does not return null - it throws. That makes sense since running out of memory is exceptional. Opening a socket accross a flaky network is more likely to fail so I could see the usefullness in returning null or some special failure flag.
>
......
>
> Yes, and then the loadImage has to check for null etc etc. Exceptions are
> a way of ensuring that "everyone checks for null" and it makes sure the
> exception happens at the point of opening the file instead of five
> routines later when someone tries to access a null reference.
> If you are concerned about the amount of code required to write a
> try/catch then you can improve on your version slightly by putting the
> return in the catch - though again I tend to think allowing the exception
> to go through loadImage is the right thing to do. Or if we could chain
> exceptions (see my earlier thread about chaining exceptions) one could
> write
>
> Image loadImage(....) {
>   MmFile f = null;
>   try { f  = new MmFile(....); }
>   catch( FileException e) {
>      throw new ImageException("Failed to load",e);
>   }
>   .....
>

I am not against exceptions in general.
They are useful when a) you need them
and b) can manage them properly.

Real scenario: HTML with images.
In this particular case 'no image' case is not
an exception - it is rather hihgly expected and
normal situation. I just cannot see reasons why
I need to pay a price of exception mechanism
when I don't need it at all.
The design of d.std should be flexible to allow me to work
in most effective way.

Again, non-existence of external resource (file, service on IP
address), etc.is not an exception.
Application should be designed  the way it
accepts existence and non-existence of resources as equally
probable outcome of open, acquisition, etc.

And yet, only at the place of opening resource you know exactly
what to do if it does not exist - e.g. apply default values
to members of the class, etc. It is just not fair to throw
your undetermination further in the code which has no
idea how to handle it.
So in real life you will *always* wrap MmFile f = new MmFile (...)
into try/catch in the procedure calling it. So why bother about
exceptions in this case?

E.g.

try
{
     Image img = LoadImage(....);
}
catch( what? ) // everything possible from Phobos?
{

}

There is no throws clause in D so you should dig inside
source to determine list of all possible exceptions.
Is it good?

Andrew.
http://terrainformatica.com








April 05, 2005
>> void is not anything. It is just nothing. http://dictionary.reference.com/search?q=void
>
> Not in D, IMO. Plus, consider "void *" how can it be a "pointer to nothing", it's actually a "pointer to anything" even in C/C++. Perhaps "void" was a bad choice of word.
>
>> See:
>> static void[12] voids;
>>
>> What is voids.sizeof ?
>
> 1

Not exactly. DMD does not allow this:
    static void[12] voids;
- sizeof cannot be determined.


>
> Set that way to allow void[] to work. And to allow any type to implicitly cast to void. eg.
>
> void foo(void[] a) {}
> void bar(byte[] a) {}
>
> void main()
> {
>   char[] ca;
>   uint[] ia;
>   long[] la;
>
>   foo(ca);  //ok
>   foo(ia);  //ok
>   foo(la);  //ok
>   bar(ca);  //error
>   bar(ia);  //error
>   bar(la);  //error
>
> }
>
> It can be quite useful.
>

I don't think that

>   long[] la;
>   foo(la);  //ok

is useful. What void foo(void[] a) {}
can do with a[] ?
But all OS file functions accepts only bytes -
addressible entities having strong physical meaning.
But AFAIK there is no way to determine number of
bytes in actual void[].

Andrew.





April 05, 2005
> And yet, only at the place of opening resource you know exactly
> what to do if it does not exist - e.g. apply default values
> to members of the class, etc. It is just not fair to throw
> your undetermination further in the code which has no
> idea how to handle it.

Actually I'd say try/catch allows you to pinpoint the location that can handle it and give that location enough context to do a good job handling it.

> So in real life you will *always* wrap MmFile f = new MmFile (...) into try/catch in the procedure calling it. So why bother about exceptions in this case?
>
> E.g.
>
> try
> {
>     Image img = LoadImage(....);
> }
> catch( what? ) // everything possible from Phobos?
> {
>
> }

One puts the try/catch wherever they make sense. Sometimes you just let the
exception go up the stack until some function wants to clean up or tell the
user or recover etc etc. There is no hard rule that exceptions must be
caught immediately.
Let me ask a question about your code example: when LoadImage returns null
(assuming that's what you would do) how do you tell the user what went
wrong? Do you say the file didn't exist or that the file length was wrong or
the colorspace wasn't supported? The code that called LoadImage has no clue
what went wrong. There would have to be a global state GetLastError (if that
wasn't already taken...) that would return the error code or string to say
exactly what happened. That is much more fragile than putting that
information in an exception that gets passed along up the call stack.

> There is no throws clause in D so you should dig inside
> source to determine list of all possible exceptions.
> Is it good?

It is part of the documentation of a function to say what it throws or does on failure. D doesn't have checked exceptions but that doesn't mean users have to guess what exceptions get thrown. Looking at the phobos doc for std.stream I notice readExact does say it throws a ReadException but File.open or File.this only says it errors but doesn't give the class of the exception. I'll make the doc more explicit about what exceptions are thrown by std.stream and send that to Walter. I notice std.mmfile does say it throws FileException for any error.


April 05, 2005
On Tue, 5 Apr 2005 14:03:53 -0700, Andrew Fedoniouk <news@terrainformatica.com> wrote:
>>> void is not anything. It is just nothing.
>>> http://dictionary.reference.com/search?q=void
>>
>> Not in D, IMO. Plus, consider "void *" how can it be a "pointer to
>> nothing", it's actually a "pointer to anything" even in C/C++. Perhaps
>> "void" was a bad choice of word.
>>
>>> See:
>>> static void[12] voids;
>>>
>>> What is voids.sizeof ?
>>
>> 1
>
> Not exactly. DMD does not allow this:
>     static void[12] voids;
> - sizeof cannot be determined.

Sorry, I miss read the question above. I thought you were asking what the size of a single void was.

It appears that static void[12] voids is illegal. But, as you can see below void[] is not, it has a size (8) as it's an array reference, and it's elements void[0] have a size also (1).

import std.stdio;

void main()
{
  //static void[12] voids; //error: integral constant must be scalar type, not void
  void[] voids;
  voids.length = 1;
  writefln(voids.sizeof);
  writefln(voids[0].sizeof);
}

Output:
8
1

>> Set that way to allow void[] to work. And to allow any type to implicitly
>> cast to void. eg.
>>
>> void foo(void[] a) {}
>> void bar(byte[] a) {}
>>
>> void main()
>> {
>>   char[] ca;
>>   uint[] ia;
>>   long[] la;
>>
>>   foo(ca);  //ok
>>   foo(ia);  //ok
>>   foo(la);  //ok
>>   bar(ca);  //error
>>   bar(ia);  //error
>>   bar(la);  //error
>>
>> }
>>
>> It can be quite useful.
>>
>
> I don't think that
>
>>   long[] la;
>>   foo(la);  //ok
>
> is useful. What void foo(void[] a) {}
> can do with a[] ?

examine/edit each byte of each "anything" (in this case long) in the array.
for any task requiring bytewise access to "anything".

> But all OS file functions accepts only bytes -
> addressible entities having strong physical meaning.
> But AFAIK there is no way to determine number of
> bytes in actual void[].

void[] stores it's own length in bytes. If you pass long[] as void[] it correctly assigns the length of the void[] to encompass all the long[] data. So, void[] has a length and it is known, in D.

Regan
April 05, 2005
This might be a completely naive comment - I don't claim to be an OO guru.  It has always bugged the heck out of me that C++ constructors must either succeed or throw, presumably this is because you can construct objects on the stack - ie Foo f(blah); - and must have a valid object.
In D the only way to construct and object is with the new keyword - ie Foo f = new Foo (blah) - and f is always a reference.  Therefore all objects in D are references, and can potentially be null.

So - would it be useful for D constructors to support the notion of failing to construct an object without throwing an exception?

example (I hope the tabs show up OK)
class Failure
{
	this(int i)
	{
		if (error_cond)
			return null;
		// otherwise setup normally
	}
}

for (int i = 0; i < 10; i++)
{
Failure f = new Failure(i);
if (f)
	add_to_list(f);
}

compared to catching exceptions
for (int i = 0; i < 10; i ++)
{
	try
	{
		Failure f = new Failure(i);
		add_to_list (f);
	}
	catch (whatever)
	{
	}
}

Does this work well with inheritance?  ie
if (!super ()) return null;

Thoughts?
Brad
April 05, 2005
On Wed, 06 Apr 2005 10:23:26 +1200, <brad@domain.invalid> wrote:
> This might be a completely naive comment - I don't claim to be an OO guru.  It has always bugged the heck out of me that C++ constructors must either succeed or throw, presumably this is because you can construct objects on the stack - ie Foo f(blah); - and must have a valid object.
> In D the only way to construct and object is with the new keyword - ie Foo f = new Foo (blah) - and f is always a reference.  Therefore all objects in D are references, and can potentially be null.
>
> So - would it be useful for D constructors to support the notion of failing to construct an object without throwing an exception?
>
> example (I hope the tabs show up OK)
> class Failure
> {
> 	this(int i)
> 	{
> 		if (error_cond)
> 			return null;
> 		// otherwise setup normally
> 	}
> }
>
> for (int i = 0; i < 10; i++)
> {
> Failure f = new Failure(i);
> if (f)
> 	add_to_list(f);
> }
>
> compared to catching exceptions
> for (int i = 0; i < 10; i ++)
> {
> 	try
> 	{
> 		Failure f = new Failure(i);
> 		add_to_list (f);
> 	}
> 	catch (whatever)
> 	{
> 	}
> }
>
> Does this work well with inheritance?  ie
> if (!super ()) return null;
>
> Thoughts?
> Brad

I think you have a valid point.

Being a C programmer I have little experience with exceptions (I have done some C++ and Java and so on), that said I am trying to use them in D, as it is the recommended practice.

I agree with Ben's point here:

<quote "Ben Hinkle">
Let me ask a question about your code example: when LoadImage returns null
(assuming that's what you would do) how do you tell the user what went
wrong? Do you say the file didn't exist or that the file length was wrong or
the colorspace wasn't supported? The code that called LoadImage has no clue
what went wrong. There would have to be a global state GetLastError (if that
wasn't already taken...) that would return the error code or string to say
exactly what happened. That is much more fragile than putting that
information in an exception that gets passed along up the call stack.
</quote>

But at the same time I agree with Andrew that sometimes having to use try/catch when a simple boolean type of "it worked"/"it failed" indication is all that is desired/required. Maybe this is simply a design problem, and where appropriate one or the other should be done, or both provided, not sure.

Can we solve this by providing a template/function to handle isolated try catch single function calls? Something like:

template call(?) { bool call(?,out Exception e)
  try {
    ? //call the function passing all args etc.
    return true;
  } catch(Exception ee) {
    e = ee;
    return false;
  }
}

allowing us to write:

Exception e;

if (!call(<function/method>,e)) {
  ..report exception..
}

as to me, that looks nicer and less hassle than the whole try/catch block.

Even better might be a built in syntax for handling this eg.

if (!call(<function/method>,e)) {
  //where 'e' is the name of the Exception and is only instantiated/valid in this block
  //no need to define it earlier as shown in first example above
}

(Instead of 'call' we could use 'catch')

In general I prefer this style of programming as I like to handle errors as they occur and avoid endlessly indenting code in {} blocks, which seems to happen when I use try/catch eg.

if () {
  try {
    if () {
      try {
      } catch() {
      }
    }
  } catch() {
  }
}

Thoughts?

Regan
April 05, 2005
One other thing that I forgot to point out in my previous post.
Exceptions are ment to handle exceptional circumstances.  With the current thinking, a constructor failing for some reason is an exceptional circumstance - is this presumption really true?
I don't see the harm in allowing constructors to fail and return NULL, all that happens is that you get a null pointer reference and segfault (BTW, I would like null pointer references to be trapped and excepted, but hey, that's another thread)

In my own C++ and D code to handle this kind of case, I usually have an empty constructor, and a worker function that fails or doesn't, so my code looks like

Foo f = new Foo();
if (f.open("whatever"))
{
....
}
// c++ only
else
   delete f;

Brad

April 05, 2005
On Wed, 06 Apr 2005 11:19:03 +1200, <brad@domain.invalid> wrote:
> One other thing that I forgot to point out in my previous post.
> Exceptions are ment to handle exceptional circumstances.  With the current thinking, a constructor failing for some reason is an exceptional circumstance - is this presumption really true?

I think whether it's "exceptional" depends on any assumptions or expectations the programmer has in each situation where it is used. So the code that throws can't know unless told whether it's expected or unexpected thus exceptional or unexceptional. The problem being that it's hard to communicate assumptions/expectations via code.

Using a previous example (and contentious subject), AA's:

foo["a"] = 4;       //assumes "a" is a key in 'foo', could throw
if ("a" in foo) {}  //a question, not an assumption, shouldn't throw

it gets more difficult to decide here:

val = foo["a"];
val = foo.get("a");

Is the programmer assuming it exists, or happy to get 'null' if it doesn't...
If the next line uses val without checking for 'null' then you have your answer, as Brad said here:

> I don't see the harm in allowing constructors to fail and return NULL, allthat happens is that you get a null pointer reference and segfault (BTW,I would like null pointer references to be trapped and excepted, but hey,that's another thread)

(I belive "constructors" in the above can be replaced with "methods" and it still applies)

Regan (perverting Brad's words/meaning to my own ends).
April 05, 2005
>> I don't think that
>>
>>>   long[] la;
>>>   foo(la);  //ok
>>
>> is useful. What void foo(void[] a) {}
>> can do with a[] ?
>
> examine/edit each byte of each "anything" (in this case long) in the
> array.
> for any task requiring bytewise access to "anything".

:) "each byte" or "each void" of "anything" ?

>
>> But all OS file functions accepts only bytes -
>> addressible entities having strong physical meaning.
>> But AFAIK there is no way to determine number of
>> bytes in actual void[].
>
> void[] stores it's own length in bytes. If you pass long[] as void[] it correctly assigns the length of the void[] to encompass all the long[] data. So, void[] has a length and it is known, in D.

void[0] is what?
void[0].sizeof shows that 'void' is in fact "third type of byte" :)




April 06, 2005
On Tue, 5 Apr 2005 16:55:38 -0700, Andrew Fedoniouk <news@terrainformatica.com> wrote:
>>> I don't think that
>>>
>>>>   long[] la;
>>>>   foo(la);  //ok
>>>
>>> is useful. What void foo(void[] a) {}
>>> can do with a[] ?
>>
>> examine/edit each byte of each "anything" (in this case long) in the
>> array.
>> for any task requiring bytewise access to "anything".
>
> :) "each byte" or "each void" of "anything" ?

:)
Sorry, badly worded. Each /byte sized/ part of "anything".

>>> But all OS file functions accepts only bytes -
>>> addressible entities having strong physical meaning.
>>> But AFAIK there is no way to determine number of
>>> bytes in actual void[].
>>
>> void[] stores it's own length in bytes. If you pass long[] as void[] it
>> correctly assigns the length of the void[] to encompass all the long[]
>> data. So, void[] has a length and it is known, in D.
>
> void[0] is what?

A /byte sized/ part of "anything".

> void[0].sizeof shows that 'void' is in fact "third type of byte" :)

It's similar to byte in that it's the same size. But I reckon D makes a slight distinction with it's implicit cast behaviour. This is my opinion and it's probably quite controversial:

<opinion>
A "byte" is a 8 bit long /self contained/ piece of memory, as in it's a complete /thing/ in 8 bits. Whereas a "void" from a void[] is an 8 bit long piece of memory, likely part of a whole /thing/.

char,  8 bits,  a whole UTF-8 codepoint (not a whole character).
byte,  8 bits,  a /thing/ which is contained in 8 bits.
short, 16 bits, a /thing/ which is contained in 16 bits.
int,   32 bits, a /thing/ which is contained in 32 bits.

void,  8 bits,  an 8 bit long part of "any/thing/".
</opinion>

Regan