From: Hector Santos on
Goran wrote:

> On Jun 24, 1:18 pm, Hector Santos <sant9...(a)nospam.gmail.com> wrote:
>> But again, you proved my point. Until you are completely aware of all
>> the possible specific exceptions traps for a class, a documentation
>> and learning issue, the "catch all" is going to be common place.
>
> Hey, I am trying to grind a different axe here!
>
> I am trying to say this: exceptions or error-return, it's both
> unrealistic and needles to be aware of / handle all possible
> exceptions / failure modes at any given place. It's easy to see why
> it's unrealistic: consider your own function somewhere in the call
> stack. Say that it can call X "base"/"system" functions, each having a
> small number of Y failure modes, some of them overlapping, for a total
> of e.g. X*Y*0.75 failure modes. Say that function is in usual win32/
> CRT form of "BOOL result + GetLastError" How do you work with that?
>
> if (!myfunc(params))
> switch (GetLastError()) { bloody hell, it's ful of cases! }
>
> ?


This is why I said, as with the simple fopen(), the most typical
errors of "interest" are literally related to:

not found
bad input
locking
sharing

any other error is more system or hardware related.

> Of course not. For the most part, you just do
>
> if (!myfunc(params))
> return false;
>
> (or equivalent thereof)


Could be, sure. For me, for example, logging functions:

FILE *fv = fopen(filename, "at");
if (fv) {
fprintf(fv,whatever);
fclose(fv);
}

but there are cases where in my more elaborate class based logging
class, where there is rotating of files, etc, there are some error
checking, including a general catch all for the valist.

> Typical error-return code looks like the above. IOW, for the most part
> (like, 90% of cases), it does not try to "handle" any failure modes at
> the place of the call.


Not quite sure if I follow, and if I did, I would not be among the
90%. I generally focus contain errors at the point of functionality. I
really use a catch all, this is more "recent" for me and only where
required, i.e, ODBC classes. I make extensive use of exceptions for
our RTE for our Wildcat! BASIC p-code language, where like a .NET
environment, you must capture programmer faults so it won't bring the
Wildcat! Interactive NET server "OS" per se.

Even in our WCBASIC language were we offer CATCH exception trapping,
the same issues apply, - do you use specific catch traps or catch
alls. It depends.

For example, since our system deals with communications, WCX (compiled
wcBASIC applets) has "Connection Drops" concepts.

If the programmer did not add any CATCH blocks, then the RTE will
abort the WCX at the next immediate OP code, cleaning up all its
managed resources.

But the programmer who needs graceful trapping or detection of
connection drop can do so as well, and for a CATCH, the RTE will jump
to that block, etc.

But the programmer can also disable the RTE connection drop detection
and allow the applet do the detection or not at all.

The latter might be useful where it knows it will complete and needs
no interruption regardless of a connection drop, or it can do a check
and gracefully clean up and exit.

etc, etc, etc.


> Well... (drum roll...) exceptions give you that behavior automatically
> (and number of failure modes does not change due to them; in that
> respect, there's no difference). But you seem to want to catch all
> these exceptions. Now I clearly have to ask: why? Just do "return
> false"! (That is, in case of exceptions, do nothing). That is the
> error of your ways WRT exceptions. No, seriously!


Again, it was more of a generalization. As I learn .NET, I will trap
catches around usage of .NET library classes, etc, because I am not
fully aware of all the possible issues with it.

Example, where I started with a global and then fine tune it.

For wcLex, the simple "get going" usage of a socket listener:


try
{
server = new TcpListener(localAddr, port);
server.Start();
worker.ReportProgress(0, "- Waiting for clients");
uint nClientTid = 0;
while (true)
{
nClientTid++;
ClientData ctcp = new ClientData(
server.AcceptTcpClient().Client,
nClientTid);
worker.ReportProgress(1, ctcp);
}
}
catch (SocketException err)
{
// 10004 expected for breaking socket handle
if (err.ErrorCode != 10004)
{
worker.ReportProgress(0,
String.Format("! SocketException: ErrorCode: {0}",
err.ErrorCode));
}
}
finally
{
// Stop listening for new clients.
worker.ReportProgress(0, "- Ending");
server.Stop();
}

The above was quick original code, and then the fine tuned to handled
to bunch of scenarios, some which only came after KNOWING what part of
the socket class does what.

For example, when it came to testing conflictive bindings (two
instances of wcLEX trying to open the same address and port), the
CATCH ALL showed the socket exception.

The question was, which call throw it?

Was it?

server = new TcpListener(localAddr, port);

or was it?

server.Start();

It was the latter, so a try was added around it to catch that specific
error event.

Since the above was part of the BackgroundWorker_DoWork event, I also
spend time to send feedback to the calling RunWorkerAsync() to above
the starting of the server. Throwing exceptions in dowork did not get
caught. I need to learn about that aspect of it. But I used mutexes
to signal a StartupOK event. Here is the code, unchanged:

private void backgroundWorkerServer_DoWork(object sender,
DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
try
{
worker.ReportProgress(0, "* Starting NNTP Server");
server = new TcpListener(localAddr, port);
server.Start();
StartupOk = true;
StartupEvent.Set();
}
catch (SocketException err)
{
if (err.SocketErrorCode ==
SocketError.AddressAlreadyInUse)
{
worker.ReportProgress(0, String.Format("! Socket
Address/Port already in use: {0}:{1}",mySettings.IPAddress,
mySettings.PortNumber));
}
else
{
worker.ReportProgress(0, String.Format("! Init
SocketException: ErrorCode: {0} {1}", err.ErrorCode,
err.SocketErrorCode));
}
StartupEvent.Set();
return;
}

try
{
worker.ReportProgress(0, "- Waiting for clients");
uint nClientTid = 0;
while (true)
{
nClientTid++;
ClientData ctcp = new
ClientData(server.AcceptTcpClient().Client, nClientTid, mySettings);
worker.ReportProgress(1, ctcp);
}
}
catch (SocketException err)
{
if (err.ErrorCode != 10004)
{
worker.ReportProgress(0, String.Format("!
SocketException: ErrorCode: {0}", err.ErrorCode));
}
}
finally
{
// Stop listening for new clients.
worker.ReportProgress(0, "- Ending");
ShutDownEvent.Set();
server.Stop();
}
}

Note, the ShutDownEvent mutex was used to synchronize an EXE shutdown
and the logging of the status becoming invalid when the application
thread is exited and thats because I switched to using in Program.cs

Form1 form1 = new Form1();
Application.Run();
GC.KeepAlive(form1);

to control the flickering of the always startup visible window made
hidden when the notifyicon is active.

So the point is, that even when you do know how the exceptions and
flow should be done, it can CHANGE depending on other factors.

See the thread where I talked about the LiveID SDK example Live ID DLL
binding with the EXE no longer applies when the DLL is bound to a
helper DLL. There the exception logic must change.

>
>> Maybe Intellisense should have a macro for extracting from a selected
>> class and wrapping try block adding all the possible specific
>> exception catch blocks.
>
> Seems like you yearn for Java checked exceptions ;-).


What IDE do you use?

--
HLS
From: Joseph M. Newcomer on
It's called a "database" and I'm sure that databases (which require the ability to append
new records) are not in the 5% category...
joe

On Wed, 23 Jun 2010 16:34:02 -0700, "David Ching" <dc(a)remove-this.dcsoft.com> wrote:

>"Joseph M. Newcomer" <newcomer(a)flounder.com> wrote in message
>news:8445269je2c1ekr6ukuj6g0e5gcj9iv2ar(a)4ax.com...
>> while
>> you are writing, anyone else should be free to write in non-append mode.
>> So if I am
>> adding record 703, there is no reason to prevent anyone from (re)writing
>> records 0..702.
>> That's why file locks exist. And they can lock the range of record 703.
>
>Interesting, thanks. I hadn't thought of allowing other processes to
>rewrite existing data while only you could append new data. That seems like
>it would be rarely used, perhaps that is the comment you got about only 5%
>of the people needed to do that?
>
>-- David
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Goran on
On Jun 24, 2:57 pm, Hector Santos <sant9...(a)nospam.gmail.com> wrote:
> > Of course not. For the most part, you just do
>
> > if (!myfunc(params))
> >   return false;
>
> > (or equivalent thereof)
>
> Could be, sure.  For me, for example, logging functions:
>
>     FILE *fv = fopen(filename, "at");
>     if (fv) {
>        fprintf(fv,whatever);
>        fclose(fv);
>     }

Yes, this is a (well known? perhaps) issue when going exceptions. You
need to turn things on their heads somewhat. Supposing that you throw,
whatever happens, somewhere down the line you have to report this
error. The best way to do this is to have nice "original" error info.
In this case, that's relatively easy, because you have file name and
errno - all is there. So for logging, there's, again, nothing to do if
you use exceptions: error will propagate to somewhere up the stack,
and.. Surely you won't forget to log it. But! But... It's
__essential__ not to lose sight of the original error and to be
comprehensive with error info.

Now, supposing that you have several places where you might get into a
particular situation, and after seeing the error in your log, you
don't know where that place is (if you will, you know the line, but
it's important t know where did you come from to it). Then, you have
two possibilities:

1. have "debug" logging, turn it on and look there to see where things
start to fall apart. Combine with error log and there you are.
2. "enhance" original error while unwinding the stack (add "context"
if you will).

At any rate, I am pretty certain that exceptions won't really stop you
from getting the desired result. But one has too stop with error-
return thinking first (don't be mad at me, but I believe you can't let
that go). Instead, you have to think this way: any given line can
throw, bar those specifically crafted not to (and they are rare and
hence +/- easy to spot). Next, you start thinking in terms of
exception-safety guarantees for any code snippet/function. For
example, any resource allocation might need "protection".

>
> but there are cases where in my more elaborate class based logging
> class, where there is rotating of files, etc, there are some error
> checking, including a general catch all for the valist.
>
> > Typical error-return code looks like the above. IOW, for the most part
> > (like, 90% of cases), it does not try to "handle" any failure modes at
> > the place of the call.
>
> Not quite sure if I follow, and if I did, I would not be among the
> 90%.

I really have to press you here: so what code do you have after a
failed call? Provide some examples, and I am pretty confident that,
even if you used exceptions, you could get what you need pretty easily
(that is, even if you "handle" errors, it's trivial to do what you do
either way, exceptions or error-return). Here's my bet: most of the
time, you have error cleanup, for which you should use RAII in e.g. C+
+, or "using" in C#, or you have logging (see above about that). In
rare places, you have "corrective" action. But I bet you that these
are __rare__.

Goran.
From: Hector Santos on

Goran wrote:

> Now, supposing that you have several places where you might get into a
> particular situation, and after seeing the error in your log, you
> don't know where that place is (if you will, you know the line, but
> it's important t know where did you come from to it). Then, you have
> two possibilities:
>
> 1. have "debug" logging, turn it on and look there to see where things
> start to fall apart. Combine with error log and there you are.
> 2. "enhance" original error while unwinding the stack (add "context"
> if you will).


Or use trace tags. :)

Goran,

I think maybe we are agreeing on the same issues. Trust me, this is
not an issue for me. Flows are not broken, exceptions are truly the
"exception" and not the rule and I have implemented different ideas
different ways, some simple, some more detailed for consumer usage.
So while I may say/write one thing, its not the be taken as its the
only way. :)

The only point I was making is that using a catch all, to me, is
generally more useful until you can chime in on specifics. Note I am
not speaking of an external the code block catch alls, but at the same
level. The external catch all only help you under debug mode since it
has source line numbering.

>> Not quite sure if I follow, and if I did, I would not be among the
>> 90%.
>
> I really have to press you here: so what code do you have after a
> failed call?


Its covered. its not ignored goran. :)

Again, just an example, a good template would be for boolean function:

bool Function(<your parameters>)
{
try
{
// whatever
return true;
} catch (Exception ex)
// do whats necessary or nothing.
}
finally
{
// do whats necessary or nothing
}
return false;
}

but it all depends goran, what if i wanted to have an overloads too?
What if you wanted to have available a boolean return or a exception
model? I have both for some functions.

> Provide some examples, and I am pretty confident that,

> even if you used exceptions, you could get what you need pretty easily
> (that is, even if you "handle" errors, it's trivial to do what you do
> either way, exceptions or error-return).


Sure, I have both. A good example was a NNTP date/time parser. The
syntax is:

yymmdd hhmmss [GMT] RFC 977
[yy]yymmdd hhmmss [GMT] RFC 3977 (replaces RFC 977)

I ended up with two parsing overloading functions

public bool GetNNTPDateTime(string sdt, out DateTime dt)
public DateTime GetNNTPDateTime(string sdt);

so I can use the first overload:

public bool onNEWGROUPS(ClientData ctcp, string args)
{
DateTime dtSince;
if (!GetNNTPDateTime(args, dtSince)) {
ctcp.SendLog("501 Invalid date/time\r\n");
return true; // RETURN ONLY FALSE TO BREAK CONNECTION
}
....
return true;
}

or I can use the exception overload version:

public bool onNEWGROUPS(ClientData ctcp, string args)
{
DateTime dtSince;
try
{
dtSince = GetNNTPDateTime(args)
}
catch (Exception ex)
{
ctcp.SendLog("501 {0}\r\n",ex.Message);
return true; // RETURN ONLY FALSE TO BREAK CONNECTION
}
....
return true;
}

But I ended up using the latter because the parser throws string
exception reasons, part of which also comes from the DateTime
converter exception trap.

At the risk of getting slammed (so be nice <g>), these are the
functions (Keep in mind that these were ported from my C/C++ NNTP
server code, which does not have the exception version).

public static DateTime GetNewGroupsDate(string sdt,
DateTimeKind wantKind =
DateTimeKind.Utc)
{

// parse parts: acceptable formats:
// YYMMDD HHMMSS [GMT]
// YYYYMMDD HHMMSS [GMT]

string[] parts = sdt.Trim().Split(' ');

if (parts.Length < 2)
{
throw new Exception("Insufficient parameters provided");
}

int len = parts[0].Length;
if (len != 6 && len != 8)
{
throw new Exception("Illegal Date - [YY]YYMMDD required");
}

int num = Convert.ToInt32(parts[0]);
int wYear = num / 10000; num -= wYear * 10000;
int wMonth = num / 100; num -= wMonth * 100;
int wDay = num;

if (len == 6) // support RFC3977 6/8 digits semantics
{
int Century = (DateTime.Now.Year / 100) * 100;
if (wYear <= (DateTime.Now.Year - Century))
{
wYear += Century;
}
else
{
wYear += Century - 100;
}
}

if (parts[1].Length != 6)
{
throw new Exception("Illegal time - HHMMSS required");
}

num = Convert.ToInt32(parts[1]);
int wHour = num / 10000; num -= wHour * 10000;
int wMinute = num / 100; num -= wMinute * 100;
int wSeconds = num;

bool gmt = false;
if (parts.Length > 2) gmt = (parts[2].ToLower() == "gmt");

DateTime dt = new DateTime(wYear, wMonth, wDay,
wHour, wMinute, wSeconds,
gmt ? DateTimeKind.Utc:DateTimeKind.Local);

if (dt.Kind == DateTimeKind.Local
&& wantKind == DateTimeKind.Utc)
return dt.ToUniversalTime();
if (dt.Kind == DateTimeKind.Utc
&& wantKind == DateTimeKind.Local)
return dt.ToLocalTime();
return dt;
}

public static bool GetNewGroupsDate(
string sdt,
out DateTime dt,
DateTimeKind wantKind = DateTimeKind.Utc)
{
dt = DateTime.MinValue;
try
{
dt = GetNewGroupsDate(sdt, wantKind);
return true;
}
catch (Exception)
{
}
return false;
}



> Here's my bet: most of the

> time, you have error cleanup, for which you should use RAII in e.g. C+
> +, or "using" in C#, or you have logging (see above about that). In
> rare places, you have "corrective" action. But I bet you that these
> are __rare__.

While I am moving towards .NET, nothing will be release (WCLEX is a
"get a deeper feel" of what to expect project) until the proper
error/memory/threading framework is worked out and well understood.

Again, because .NET is richly complex and does so much for you, the
developers need to get a handle of whats already done for you or not.
Probably the less you know the better. :) But thats not the case here.
I have language-independent expectations for well understood solid
behavior. Files, sockets, etc, all work one way - how its implemented
is what needs to be understood.

What that means I must have my "managed" exceptions worked out but I
will now under .NET, allow on "catch alls" to cover the unexpected.

The thing is under .NET you almost always have catch all at the top
level if you use the auto-generated main program code, so that means
that your own catch all is to keep the application running for the
most part, and that can hurt you too.

If anything, that is the big key thing here - do you abort or
gracefully recover for the unexpected catch alls?

This applies to any framework and language and these ideas are 100%
application implementation specific. There are no hard rule. There are
better rules, but never ALL cut and dry.

--
HLS
From: David Ching on
"Joseph M. Newcomer" <newcomer(a)flounder.com> wrote in message
news:avn626hdrqo5mirsnvqg38a5e32v052sd1(a)4ax.com...
> It's called a "database" and I'm sure that databases (which require the
> ability to append
> new records) are not in the 5% category...

I had not considered that. But I doubt very much that databases uses .NET
StreamWriter!

-- David