From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,699cc914522aa7c4 X-Google-Attributes: gid103376,public X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news2.google.com!news4.google.com!border1.nntp.dca.giganews.com!border2.nntp.dca.giganews.com!nntp.giganews.com!out02b.usenetserver.com!news.usenetserver.com!in02.usenetserver.com!news.usenetserver.com!in03.usenetserver.com!news.usenetserver.com!pc03.usenetserver.com!news.flashnewsgroups.com-b7.4zTQh5tI3A!not-for-mail Newsgroups: comp.lang.ada Subject: Re: Structured exception information References: <1169819196.5976.57.camel@localhost.localdomain> From: Stephen Leake Date: Sat, 27 Jan 2007 13:17:02 -0500 Message-ID: User-Agent: Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3 (windows-nt) Cancel-Lock: sha1:zl8tRX3qvvEPd5AY6xME8gU5tkY= MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Complaints-To: abuse@flashnewsgroups.com Organization: FlashNewsgroups.com X-Trace: c5eb045bb9727759e00d413289 Xref: g2news2.google.com comp.lang.ada:8626 Date: 2007-01-27T13:17:02-05:00 List-Id: Georg Bauhaus writes: > On Fri, 2007-01-26 at 04:35 -0500, Stephen Leake wrote: > > I'd prefer something like > > procedure Write_Output > (Module : in Module_Type; > Symbol : in Symbol_Access_Type) > is > User_Volts : constant Real_Type := Reals.Get (Symbol); > begin > if User_Volts > Max_Volts then > View.Set_Error((Off_Error with > Condition => Greater_Than, > Required => Max_Volts, > Given => User_Volts)); > raise User_Error; > else > Write_Hardware (Module.Hardware_Device, To_Counts (User_Volts)); > end if; > > end Write_Output; > > And then > > exception > when E : User_Error => > View.Show_Error; > -- Uses the interface of the progenitors of Off_Error, > -- for the object entered using View.Set_Error. > -- If we had exception types, this could be > -- much simpler, just View.Show_Error(E). > View.Print(Ada.Exceptions.Get_Exception_Message (E)); > end; > Obviously that would work, but _why_ is it "better"? In my opinion, it's worse, for libraries you want to distribute; you now have this non-standard "View" library to distribute with your application. I guess View.Show_Error might do something other than write a string to an error log. It might put up a structured dialog box, with widgets for each different field of the exception type. But that's a huge amount of work, and no application I've ever seen does that. What do you think Show_Error does here? In addition, you have ignored the problem that each version of Write_Output for different hardware will need need a corresponding version of the exception type; more work. Hmm. With the choice of names you used in Set_Error, you seem to be implying that there is a "general error description language", that Set_Error can enforce. I don't think that's true. Here is a small sample of actual error messages from my application: raise Hardware_Error with "digital pot card did not respond to serial number request"; raise Hardware_Error with "digital pot card responded with wrong byte count to serial number" & request (got" & Stream_Element_Offset'Image (Data_Last) & ")"; raise Parameter_Error with "motor" & Integer'Image (Module.Ramp_Motor_Index) & " Disable_Encoder_Model not set"; raise Device_Error with "can't open device " & C_Name (1 .. C_Name'Last - 1) & " : " & Error_Type'Image (To_Error (GNAT.OS_Lib.Errno)); (There are many, many more, of course). Here are a couple of other 'validation' procedures that raise exceptions: procedure Check_Size (Max : in Integer) is begin if Integer (Bit) + Integer (Bit_Size) > Max then Ada.Exceptions.Raise_Exception (Config_File_Error'Identity, File_Line_Column & " bit, bit_size overflow register"); end if; end Check_Size; procedure Check_Direction (Port : in Port_Type) is begin if Direction /= Mode (Port) then Ada.Exceptions.Raise_Exception (Config_File_Error'Identity, File_Line_Column & " conflicting directions"); end if; end Check_Direction; I suppose it might be _possible_ to come up with a generic set of exception type components that would cover all of the messages in my application, but that's a lot of extra design work, when 'raise ... with " give the same end result much more simply. > Absent exception types, making exception info propagation > explicit will add some advantages, in addition to not being > implicit as in exception strings: > > - message information isn't dependent on arbitrary strings in the > implementation of Write_Output. You'd need to recompile a > *validation* procedure I guess by "validation" you mean the check "user_volts > Max_volts", and the Check_* procedures I quoted above. > when the *message* should change even though the validation didn't > change. From this viewpoint Write_Output mixes two separate > concerns: input checking and UI message construction. People keep saying those should be separate concerns, but I don't accept that. Why is it _better_ that they be separate? As far as I can tell, the _primary_ purpose of a validation procedure is to report a problem to the user. Formatting a string is the best way to do that. > - Off_Error is derived from an abstract data type describing > failure information. A user of this type can choose a > suitable message constructed from the information. > This is both *MVC* and also *decoupling*: The procedure Write_Output > can be used unchanged even when error messages are to be > displayed using Wide_String, Converting String to Wide_String is trivial. > a pair of LEDs No, there is nowhere near enough expressive power in LEDs to cover all possible error messages. This is not a viable option. That's like saying "I can browse the web on my cell phone". You may have a cell phone that understands http and can format HTML, but you can actually _use_ only the very small fraction of the web sites that have been reworked to support small displays. This is an excellent analogy; the _provider_ of the information has to understand the limits of the _display_. Yes, I am assuming something about the ultimate error display mechanism; I'm assuming it can display String. But no-one has presented an actual alternative that isn't equivalent to String! > or a Spanish character set. I think that is a valid objection; other parts of this thread have talked around it. My application has no (current :) requirement for this, so I can ignore it. But let me present what I might do if this became a real requirement. I would try to use the Gnu gettext library. I believe (I have _not_ studied this in detail) that library allows you to define "message_ids" that are then looked up in a message database; there are different instances of the database for each language. In addition, I think you can include C printf escapes in the strings. So two of the messages above would become: raise Hardware_Error with MESSAGE_ID_1234; where the English version of MESSAGE_ID_1234 is "digital pot card did not respond to serial number request" raise Device_Error with GetText (MESSAGE_ID_5678, C_Name (1 .. C_Name'Last - 1), Error_Type'Image (To_Error (GNAT.OS_Lib.Errno)); The English version of MESSAGE_ID_5678 would be: "can't open device %s : %s"; Note that a language that requires different noun/verb ordering can use this approach quite nicely. That seems to be a reasonable approach, and my current application could be incrementally changed to accomodate it. Anyone that wants to port my application to Spanish then just has to translate the provided English strings to Spanish. Ah; it might be that the new language _requires_ a 16 bit character set (Chinese, for example). Then we have to assume UTF-8 encoding. It might be simpler if 'raise' supported Wide_String (or Wide_Wide_String?) directly, but it's not an insurmountable problem. Perhaps Ada 2015 will allow Wide_Wide_String for exceptions. > The construction of a message that meets the UI criteria (the parser > loop with prompt in this case) becomes the job of the a View package > that matches the display. The _primary_ role of a parser is to accept user input and provide feedback on errors. Why is it _better_ to move part of that job over into the View package? > - the program points the reader to the software types and > modules involved in propagating error information by > naming the package and type at both ends (View in this case). > A change in the implementation of View won't necessitate > a recompilation of Write_Output, unlike using > raise ... with ... for message construction. Anticipating a change in implementation is a good reason for separating out packages. However, I have yet to see an _actual_ implemention of View that is not equivalent to "write this string to a device". So I do _not_ anticipate changing that implementation. -- -- Stephe