www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - std.mmfile.d observations

reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
First:

In current implementation of MmFile (std.mmfile.d)
constructor of MmFile throws exceptions if file cannot be opened.

I don't think that throwing exceptions in cunstructors is a good idea
at all.

MmFile, I think, shall have not public constructors. Instead
it should have one static method:

static MmFile open(pathname, openmode) {...}

which does not throw any errors and returns just null if operation
cannot be completed.

Having implemented MmFile this way user of it will always
get valid MmFile object in valid state. This is more deterministic
approach I guess.

Phylosophically speaking, negative response on
any file open operation is not an exception but
rather expected situation.
So don't need an exception here. IMHO of course.

--------------------------------------------------------
Second:

These two methods of MmFile are in conflict by their return values.

void[] opSlice(size_t i1, size_t i2)
ubyte opIndex(size_t i)

(And yet I still didn't get an idea what void[] means
- vector of nothings? )

Probably it is better to change it to
ubyte[] opSlice(size_t i1, size_t i2) for the sake of simplicity?

Andrew.
http://terrainformatica.com
Apr 04 2005
next sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Andrew Fedoniouk" <news terrainformatica.com> wrote in message 
news:d2s9kn$460$1 digitaldaemon.com...
 First:

 In current implementation of MmFile (std.mmfile.d)
 constructor of MmFile throws exceptions if file cannot be opened.

Note std.stream also throws when you try to construct a File or BufferedFile and it fails to open or create. Java and .Net also throw.
 I don't think that throwing exceptions in cunstructors is a good idea
 at all.

I suppose it is nasty if one is subclassing and trying to practice RAII. Is that what you had in mind? Maybe we should just make sure these things are final classes and cut the problems off from the start.
 MmFile, I think, shall have not public constructors. Instead
 it should have one static method:

 static MmFile open(pathname, openmode) {...}

 which does not throw any errors and returns just null if operation
 cannot be completed.

That isn't too bad, I suppose. Checking for a special return value is a common way to avoid throwing. If anyone tries to use the resulting MmFile on failure they'll get a seg-v, which might be less obvious what happened than an exception thrown from MmFile.
 Having implemented MmFile this way user of it will always
 get valid MmFile object in valid state. This is more deterministic
 approach I guess.

How does throwing return an object in an invalid state? It seems just as deterministic to me.
 Phylosophically speaking, negative response on
 any file open operation is not an exception but
 rather expected situation.
 So don't need an exception here. IMHO of course.

Maybe we need "open" and "open_plz" where open_plz is less ... assertive. :-)
 --------------------------------------------------------
 Second:

 These two methods of MmFile are in conflict by their return values.

 void[] opSlice(size_t i1, size_t i2)
 ubyte opIndex(size_t i)

 (And yet I still didn't get an idea what void[] means
 - vector of nothings? )

 Probably it is better to change it to
 ubyte[] opSlice(size_t i1, size_t i2) for the sake of simplicity?

Seems like a good idea to me.
Apr 04 2005
next sibling parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
Below:

 In current implementation of MmFile (std.mmfile.d)
 constructor of MmFile throws exceptions if file cannot be opened.

Note std.stream also throws when you try to construct a File or BufferedFile and it fails to open or create. Java and .Net also throw.

And it is bad (IMHO). What happens with this allocated and not constructed object? It just becomes "joy of garbage colector" which acceptable in pure GC based sytems ... But use cases of D are not always "GCable".
 I don't think that throwing exceptions in cunstructors is a good idea
 at all.

I suppose it is nasty if one is subclassing and trying to practice RAII. Is that what you had in mind? Maybe we should just make sure these things are final classes and cut the problems off from the start.

Probably I am too deep in C++ where exceptions in constructors are "persons non grata" and, apropos, bad design. But anyway it is better to have valid object or nothing then nobody knows what.
 MmFile, I think, shall have not public constructors. Instead
 it should have one static method:

 static MmFile open(pathname, openmode) {...}

 which does not throw any errors and returns just null if operation
 cannot be completed.

That isn't too bad, I suppose. Checking for a special return value is a common way to avoid throwing. If anyone tries to use the resulting MmFile on failure they'll get a seg-v, which might be less obvious what happened than an exception thrown from MmFile.

Semantic of 'open' operation always assumes negative results too: (as access to any other system/external resource) Example: Image loadImage(....) { MmFile f = MmFile.open(....); if( f is null ) return null; .... ---versus current ------------------ Image loadImage(....) { MmFile f = null; try { f = new MmFile(....); } catch( FileException ) { } if( f is null ) return null; .....
 Having implemented MmFile this way user of it will always
 get valid MmFile object in valid state. This is more deterministic
 approach I guess.

How does throwing return an object in an invalid state? It seems just as deterministic to me.

System first allocates chunk of memory. If exception happens then this memory is just non-determenistic garbage :)
 Phylosophically speaking, negative response on
 any file open operation is not an exception but
 rather expected situation.
 So don't need an exception here. IMHO of course.

Maybe we need "open" and "open_plz" where open_plz is less ... assertive. :-)

:-))) Program in INTERCAL programming language: DO (5) NEXT (5) DO FORGET #1 PLEASE WRITE IN :1 DO .1 <- 'V":1~'#32768$#0'"$#1'~#3 DO (1) NEXT DO :1 <- "'V":1~'#65535$#0'"$#65535' ~'#0$#65535'"$"'V":1~'#0$#65535'" $#65535'~'#0$#65535'" DO :2 <- #1 PLEASE DO (4) NEXT (4) DO FORGET #1 DO .1 <- "'V":1~'#65535$#0'"$":2~'#65535 $#0'"'~'#0$#65535'"$"'V":1~'#0 $#65535'"$":2~'#65535$#0'"'~'#0$#65535'" DO (1) NEXT DO :2 <- ":2~'#0$#65535'" $"'":2~'#65535$#0'"$#0'~'#32767$#1'" DO (4) NEXT (2) DO RESUME .1 (1) PLEASE DO (2) NEXT PLEASE FORGET #1 DO READ OUT :1 PLEASE DO .1 <- 'V"':1~:1'~#1"$#1'~#3 DO (3) NEXT PLEASE DO (5) NEXT (3) DO (2) NEXT PLEASE GIVE UP -------------------------- Last statement especially .... touching I would say.
Apr 04 2005
next sibling parent reply "Matthew" <admin.hat stlsoft.dot.org> writes:
 Probably I am too deep in C++ where exceptions in constructors
 are "persons non grata" and, apropos, bad design.

Really? Since when? Please offer some sources to substantiate this opinion.
Apr 04 2005
parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
 Probably I am too deep in C++ where exceptions in constructors
 are "persons non grata" and, apropos, bad design.

Really? Since when? Please offer some sources to substantiate this opinion.

Sorry, Matthew, I've propagated problem with exceptions in destructors [1] onto constructors also. Mea culpa. BTW: MmFile destructor is also generating exceptions. In any case general rule: "you should avoid unnecessary try blocks" [2] sounds reasonable as a general recommendation. IMHO, System resource wrappers should not throw exceptions or at least should allow non exceptional forms of operations. There are too many practical reasons for that. One possible implemetataion: class MmFile { this() {} // constructs empty wrapper, no exceptions this(pathname) { if( !open(pathname) ) throw FileException(...). } // the only way to report problem in ctor is to throw an exception. bool open( pathname) // tries to open physical object, no exception here. bool close( ) // tries to 'close' physical object, no exception here. ~this() { close(); } // no exception here, ignorabimus et ignoramus } But I am personally prefer the form : class MmFile { potected this() {} // protected, not constructable in 'void' state externally static MmFile open( pathname); // factory alike ctor. // tries to open physical object, no exception here. // returns null if MmFile could not be open in requested state. bool close( ) // tries to 'close' physical object, no exception here. ~this() { close(); } // no exception here } The second form does not even try to allocate any object if it cannot be created. If I (consumer of MmFile) need an exception I can always throw them after failed open. I would like to have an option to manage throw or not to throw problem by myself. Andrew Fedoniouk. http://terrainformatica.com Literature: [1] Scott Meyer, More Effective C++. "Item 11: Prevent exceptions from leaving destructors." ( http://www.ishiboo.com/~nirva/c++/eff/MEC/MI11_FR.HTM ) [2] Scott Meyer, More Effective C++. "Item 15: Understand the costs of exception handling"
Apr 05 2005
parent reply "Matthew" <admin.hat stlsoft.dot.org> writes:
"Andrew Fedoniouk" <news terrainformatica.com> wrote in message
news:d2uorf$2ot6$1 digitaldaemon.com...
 Probably I am too deep in C++ where exceptions in constructors
 are "persons non grata" and, apropos, bad design.

Really? Since when? Please offer some sources to substantiate this opinion.

Sorry, Matthew, I've propagated problem with exceptions in destructors [1] onto constructors also. Mea culpa.

Ok. Phew! <g> Of course, throwing exceptions from dtors in C++ is an (almost) absolute no-no.
 BTW:  MmFile destructor is also generating exceptions.

Sigh. I don't doubt it. I need to rewrite it
 In any case general rule: "you should avoid unnecessary try blocks" [2]
 sounds reasonable as a general recommendation.

 IMHO, System resource wrappers should not throw exceptions
 or at least should allow non exceptional forms of operations.

I totally agree. I instinctively go for a two layer approach - vestigial evidence of which can be seen in std.loader - whereby non/little-throwing core functions are used in throwing class layers. I write my C/C++ in this way, and don't see anything fundamentally different in D that it should be otherwise. (Of course, such an approach is open to being criticised as being overly complex. C'est la vie!) In that way, there's full and proper support for both procedural and OO programming, without any bias/disaccommodation to practitioners in either camp.
Apr 05 2005
parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
 In that way, there's full and proper support for both procedural and OO 
 programming, without any bias/disaccommodation
 to practitioners in either camp.

This 'and' in "procedural and OO programming" sounds like murmur of Styx river separating these two kingdoms. Where Matthew is like Charon on duty. :) ( No offence ) Literature: [1] http://www.pantheon.org/articles/s/styx_river.html
Apr 07 2005
parent "Matthew" <admin.hat stlsoft.dot.org> writes:
"Andrew Fedoniouk" <news terrainformatica.com> wrote in message
news:d32mf5$1b65$1 digitaldaemon.com...
 In that way, there's full and proper support for both procedural and OO
programming, without any 
 bias/disaccommodation
 to practitioners in either camp.

This 'and' in "procedural and OO programming" sounds like murmur of Styx river separating these two kingdoms. Where Matthew is like Charon on duty. :) ( No offence )

LOL! Brilliant. I shall be thus known henceforth. :-)
Apr 07 2005
prev sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Andrew Fedoniouk" <news terrainformatica.com> wrote in message 
news:d2t7qo$12m7$1 digitaldaemon.com...
 Below:

 In current implementation of MmFile (std.mmfile.d)
 constructor of MmFile throws exceptions if file cannot be opened.

Note std.stream also throws when you try to construct a File or BufferedFile and it fails to open or create. Java and .Net also throw.

And it is bad (IMHO). What happens with this allocated and not constructed object? It just becomes "joy of garbage colector" which acceptable in pure GC based sytems ... But use cases of D are not always "GCable".

If one is running D code without a GC they have more things in Phobos to worry about than a leak from an exception. Several parts of the library would have to change APIs to adjust. It would probably be easier to not use phobos at all and start a GC-less API elsewhere.
 I don't think that throwing exceptions in cunstructors is a good idea
 at all.

I suppose it is nasty if one is subclassing and trying to practice RAII. Is that what you had in mind? Maybe we should just make sure these things are final classes and cut the problems off from the start.

Probably I am too deep in C++ where exceptions in constructors are "persons non grata" and, apropos, bad design. But anyway it is better to have valid object or nothing then nobody knows what.
 MmFile, I think, shall have not public constructors. Instead
 it should have one static method:

 static MmFile open(pathname, openmode) {...}

 which does not throw any errors and returns just null if operation
 cannot be completed.

That isn't too bad, I suppose. Checking for a special return value is a common way to avoid throwing. If anyone tries to use the resulting MmFile on failure they'll get a seg-v, which might be less obvious what happened than an exception thrown from MmFile.

Semantic of 'open' operation always assumes negative results too: (as access to any other system/external resource)

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.
 Example:

 Image loadImage(....) {
    MmFile f = MmFile.open(....);
    if( f is null ) return null;
    ....
 ---versus current ------------------

 Image loadImage(....) {
   MmFile f = null;
   try { f  = new MmFile(....); }
   catch( FileException ) {  }
   if( f is null ) return null;
   .....

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); } .....
 Having implemented MmFile this way user of it will always
 get valid MmFile object in valid state. This is more deterministic
 approach I guess.

How does throwing return an object in an invalid state? It seems just as deterministic to me.

System first allocates chunk of memory. If exception happens then this memory is just non-determenistic garbage :)

Free your mind, not your memory :-) Don't worry about a block or two that become garbage from an exception - it won't happen often enough to be measurable.
 Phylosophically speaking, negative response on
 any file open operation is not an exception but
 rather expected situation.
 So don't need an exception here. IMHO of course.

Maybe we need "open" and "open_plz" where open_plz is less ... assertive. :-)

:-))) Program in INTERCAL programming language: DO (5) NEXT (5) DO FORGET #1 PLEASE WRITE IN :1 DO .1 <- 'V":1~'#32768$#0'"$#1'~#3 DO (1) NEXT DO :1 <- "'V":1~'#65535$#0'"$#65535' ~'#0$#65535'"$"'V":1~'#0$#65535'" $#65535'~'#0$#65535'" DO :2 <- #1 PLEASE DO (4) NEXT (4) DO FORGET #1 DO .1 <- "'V":1~'#65535$#0'"$":2~'#65535 $#0'"'~'#0$#65535'"$"'V":1~'#0 $#65535'"$":2~'#65535$#0'"'~'#0$#65535'" DO (1) NEXT DO :2 <- ":2~'#0$#65535'" $"'":2~'#65535$#0'"$#0'~'#32767$#1'" DO (4) NEXT (2) DO RESUME .1 (1) PLEASE DO (2) NEXT PLEASE FORGET #1 DO READ OUT :1 PLEASE DO .1 <- 'V"':1~:1'~#1"$#1'~#3 DO (3) NEXT PLEASE DO (5) NEXT (3) DO (2) NEXT PLEASE GIVE UP -------------------------- Last statement especially .... touching I would say.

Nice. It's like getting a "System panic" message on a Sun box. If the system is panicing, then what should I be doing? yikes.
Apr 05 2005
parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
 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
Apr 05 2005
parent "Ben Hinkle" <bhinkle mathworks.com> writes:
 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.
Apr 05 2005
prev sibling parent reply brad domain.invalid writes:
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
Apr 05 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
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
Apr 05 2005
parent reply brad domain.invalid writes:
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
Apr 05 2005
parent "Regan Heath" <regan netwin.co.nz> writes:
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).
Apr 05 2005
prev sibling next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Mon, 4 Apr 2005 13:55:52 -0700, Andrew Fedoniouk  
<news terrainformatica.com> wrote:
 These two methods of MmFile are in conflict by their return values.

 void[] opSlice(size_t i1, size_t i2)
 ubyte opIndex(size_t i)

 (And yet I still didn't get an idea what void[] means
 - vector of nothings? )

My impression is that given that void[] is implicitly castable to any other array type, void[] means "vector of anythings". Regan
Apr 04 2005
parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
 These two methods of MmFile are in conflict by their return values.

 void[] opSlice(size_t i1, size_t i2)
 ubyte opIndex(size_t i)

 (And yet I still didn't get an idea what void[] means
 - vector of nothings? )

My impression is that given that void[] is implicitly castable to any other array type, void[] means "vector of anythings".

void is not anything. It is just nothing. http://dictionary.reference.com/search?q=void See: static void[12] voids; What is voids.sizeof ?
Apr 04 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Mon, 4 Apr 2005 22:39:24 -0700, Andrew Fedoniouk  
<news terrainformatica.com> wrote:
 These two methods of MmFile are in conflict by their return values.

 void[] opSlice(size_t i1, size_t i2)
 ubyte opIndex(size_t i)

 (And yet I still didn't get an idea what void[] means
 - vector of nothings? )

My impression is that given that void[] is implicitly castable to any other array type, void[] means "vector of anythings".

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 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. Regan
Apr 04 2005
parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
 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.
Apr 05 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
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
Apr 05 2005
parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
 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" :)
Apr 05 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
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
Apr 05 2005
parent Georg Wrede <georg.wrede nospam.org> writes:
Regan Heath wrote:
 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/.

One could think of void as being an address only. And since such don't need / can't know, what we imagine there being, then the size is 1. Actually, the size should be 0 to emphasize this, bat technically that would become cumbersome, since you most often itreate through ranges of voids. With size 0 there would have to be extra measures created in the compiler (and philosophy) to enable this iteration to actually move forward. So, for practical reasons, void is of size 1. Another way of putting it is, we do need occasionally to address memory without knowing/caring about the type of data there is. A byte being the smallest location traditionally addressable by programming languages, it is natural that "this smallest address-difference" is used as the "size of void".
Apr 05 2005
prev sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Andrew Fedoniouk wrote:
<snip>
 MmFile, I think, shall have not public constructors. Instead
 it should have one static method:
 
 static MmFile open(pathname, openmode) {...}
 
 which does not throw any errors and returns just null if operation
 cannot be completed.

No. What would be the point of having exceptions if we're going to go out of way not to use them? http://www.digitalmars.com/d/errors.html Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Apr 05 2005