I use an object based on a base package roughly defined as:
package Base is
type T_Base is abstract tagged null record;
-- This performs a deep copy. Shallow copies may lead to STORAGE_ERROR.
-- This shall be implemented by every derived type.
function Copy (From : in T_Base) return T_Base'Class is abstract;
end package Base;
This package is derived by several packages which are further derived
package Foo is
type T_Foo is new T_Base with record
A_Data : Natural; -- Of course, in the real code, these are types by far more complex.
end record;
procedure do_something (Foo_Object : in T_Foo);
-- This implements the deep copy
function Copy (From : in T_Foo) return T_Base'Class is abstract;
end package Foo;
On calling the procedure do_something, I do get a storage_error:
procedure handle_received_foo (Foo_In: in Foo.T_Foo) is
begin
Foo.do_something (Foo_Object => Foo_In); -- The storage error does happen here.
end main;
When running the code with gdb, I get a segfault on entering the function and I get:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 39 (LWP 39)]
0x033c9828 in foo.do_something (foo_object=...) at ./foo.adb:67
67 procedure do_something (Foo_Object : in T_Foo);
(gdb) p foo_object
$1 (null)
So I guess I get a storage_error when doing the shallow copy of the argument Foo_Object.
I am aware that this is no MWE and that there might be a mistake in one of the types present used in the derived types.
I can't find any good option:
Making T_Foo a Controlled type to call Copy in Adjust seems not to be possible without greatly changing its definition as I can't derive T_Foo both from T_Base and Ada.Finalization.Controlled since none of them is an interface types
Defining T_Base as
type T_Base is abstract new Ada.Finalization.Controlled with null record;
and override Adjust here seems to induce a hell lot too much modifications on the existing code base as gnat yields in multiple places
type of aggregate has private ancestor "Controlled" must use extension aggregate.
So I'm low on solutions to either investigate the problem further or to solve it with a hammer.
The problem was not in the Copy function. The comments I saw in the code base were misleading.
The fact that introducing new variables changed the location of the exception made me consider some stack overflow problems.
Indeed the Storage_Size allocated for the task was not sufficient. Increasing the pragma Storage_Size(<value>) solved the problem.
Since the code base is compiled with -fstack-check, this led to the aforementioned STORAGE_ERROR.
More infos on Adacore's documentation.
This could probably have been seen with Gem #95: Dynamic Stack Analysis in GNAT but I am not currently able to see any result with the binding option suggested therein.
EDIT: The original question had unnecessary details
I have a source file which I do value analysis in Frama-C, some of the code is highlighted as dead code in the normalized window, no the original source code.
Can I obtain a slice of the original code that removes the dead code?
Short answer: there's nothing in the current Frama-C version that will let you do that directly. Moreover, if your original code contains macros, Frama-C will not even see the real original code, as it relies on an external preprocessor (e.g. cpp) to do macro expansion.
Longer answer: Each statement in the normalized (aka CIL) Abstract Syntax Tree (AST, the internal representation of C code within Frama-C) contains information about the location (start point and end point) of the original statement where it stems from, and this information is also available in the original AST (aka Cabs). It might thus be possible for someone with a good knowledge of Frama-C's inner workings (e.g. a reader of the developer's manual), to build a correspondance between both, and to use that to detect dead statement in Cabs. Going even further, one could bypass Cabs, and identify zones in the original text of the program which are dead code. Note however that it would be a tedious and quite error prone (notably because a single original statement can be expanded in several normalized ones) task.
Given your clarifications, I stand by #Virgile's answer; but for people interested in performing some simplistic dead code elimination within Frama-C, the script below, gifted by a colleague who has no SO account, could be helpful.
(* remove_dead_code.ml *)
let main () =
!Db.Value.compute ();
Slicing.Api.Project.reset_slicing ();
let selection = ref Slicing.Api.Select.empty_selects in
let o = object (self)
inherit Visitor.frama_c_inplace
method !vstmt_aux stmt =
if Db.Value.is_reachable_stmt stmt then
selection :=
Slicing.Api.Select.select_stmt ~spare:true
!selection
stmt
(Extlib.the self#current_kf);
Cil.DoChildren
end in
Visitor.visitFramacFileSameGlobals o (Ast.get ());
Slicing.Api.Request.add_persistent_selection !selection;
Slicing.Api.Request.apply_all_internal ();
Slicing.Api.Slice.remove_uncalled ();
ignore (Slicing.Api.Project.extract "no-dead")
let () = Db.Main.extend main
Usage:
frama-c -load-script remove_dead_code.ml file.c -then-last -print -ocode output.c
Note that this script does not work in all cases and could have further improvements (e.g. to handle initializers), but for some quick-and-dirty hacking, it can still be helpful.
I'm trying to generate a random Positive in ada in my procedure. For this, I have the following code:
procedure Inicialize(K: Positive) is
package rand is new ada.numerics.discrete_random(Positive);
use rand;
G: Generator;
t: Positive;
begin
isInitialized:= True;
reset(G);
t := random(G); --error for this line
end Inicialize;
I get the error
incorrect use of "random"
I have no idea, how is this an incorrect use, or what does it even mean. Could someone please elaborate?
Thank you!
Assuming isInitialized is a global variable, that code looks legal as it stands. It compiles and runs correctly with a current Gnat Ada. Is the full procedure more complicated than this? Could G or random be being redeclared?
I see your earlier question which mentions a package random. So it's likely a name clash between the package name and the procedure name. Removing the 'use rand', or renaming the package, would cure this.
I want to print integers in Ada 83. At present I am just using 'with Text_IO' and 'use Text_IO'. I don't want to print using the Integer'Image option. I want to use Integer_Text_IO in ada83. Please help me out with the syntax.
I am using below code:
with Text_IO;
use Text_IO;
i: INTEGER :=1;
package Int_IO is new Integer_IO(INTEGER);
use Int_IO; put(i);
I am getting 'expect signed integer type in instantiation of "Num" ' error.
The example below, which compiles, should help.
But please, when posting a question on StackOverflow (or anywhere on the Net, really) show us the code you’ve actually tried. The sample you’ve provided doesn’t come close to compiling (it fails at line 3 with compilation unit expected), and that makes it very hard for us to work out how to help you.
You’ll get expect signed integer type in instantiation of “Num” if you try to instantiate Text_IO with the wrong sort of type (for example, Float).
with Text_IO;
procedure Integer_IO_Demo is
package Int_IO is new Text_IO.Integer_IO (Integer);
begin
for J in 5 .. 10 loop
Int_IO.Put (J);
Text_IO.New_Line;
end loop;
end Integer_IO_Demo;
What is good practice for error handling in math-related functions? I'm building up a library (module) of specialized functions and my main purpose is to make debugging easier for the code calling these functions -- not to make a shiny user-friendly error handling facility.
Below is a simple example in VBA, but I'm interested in hearing from other languages as well. I'm not quite sure where I should be returning an error message/status/flag. As an extra argument?
Function AddArrays(arr1, arr2)
Dim i As Long
Dim result As Variant
' Some error trapping code here, e.g.
' - Are input arrays of same size?
' - Are input arrays numeric? (can't add strings, objects...)
' - Etc.
' If no errors found, do the actual work...
ReDim result(LBound(arr1) To UBound(arr1))
For i = LBound(arr1) To UBound(arr1)
result(i) = arr1(i) + arr2(i)
Next i
AddArrays = result
End Function
or something like the following. The function returns a boolean "success" flag (as in the example below, which would return False if the input arrays weren't numeric etc.), or an error number/message of some other type.
Function AddArrays(arr1, arr2, result) As Boolean
' same code as above
AddArrays = booSuccess
End Function
However I'm not too crazy about this, since it ruins the nice and readable calling syntax, i.e. can't say c = AddArrays(a,b) anymore.
I'm open to suggestions!
Obviously error handling in general is a big topic, and what the best practice is depends a lot on the capabilities of the language you're working with and how the routine you're coding fits in with other routines. So I'll constrain my answer to VBA (used within Excel) and library-type routines of the sort you're describing.
Exceptions vs. Error Codes in Library Routines
In this case, I would not use a return code. VBA supports a form of exception handling that, while not as powerful as the more standard form found in C++/Java/??.NET, is pretty similar. So the advice from those languages generally applies. You use exceptions to tell calling routines that the called routine can't do it's job for whatever reason. You handle exceptions at the lowest level where you can do something meaningful about that failue.
Bjarne Stroustrup gives a very good explanation of why exceptions are better than error codes for this kind of situation in this book. (The book is about C++, but the principles behind C++ exception handling and VBA error handling are the same.)
http://www2.research.att.com/~bs/3rd.html
Here is a nice excerpt from Section 8.3:
When a program is composed of separate
modules, and especially when those
modules come from separately developed
libraries, error handling needs to be
separated into two distinct parts: [1]
The reporting of error conditions that
cannot be resolved locally [2] The
handling of errors detected elsewhere
The author of a library can detect
runtime errors but does not in general
have any idea what to do about them.
The user of a library may know how to
cope with such errors but cannot
detect them – or else they would be
handled in the user’s code and not
left for the library to find.
Sections 14.1 and 14.9 also address exceptions vs. error codes in a library context. (There is a copy of the book online at archive.org.)
There is probably lots more about this on stackoverflow. I just found this, for example:
Exception vs. error-code vs. assert
(There can be pitfalls involving proper management of resources that must be cleaned up when using exceptions, but they don't really apply here.)
Exceptions in VBA
Here is how raising an exception looks in VBA (although the VBA terminology is "raising an error"):
Function AddArrays(arr1, arr2)
Dim i As Long
Dim result As Variant
' Some error finding code here, e.g.
' - Are input arrays of same size?
' - Are input arrays numeric? (can't add strings, objects...)
' - Etc.
'Assume errorsFound is a variable you populated above...
If errorsFound Then
Call Err.Raise(SOME_BAD_INPUT_CONSTANT) 'See help about the VBA Err object. (SOME_BAD_INPUT_CONSTANT is something you would have defined.)
End If
' If no errors found, do the actual work...
ReDim result(LBound(arr1) To UBound(arr1))
For i = LBound(arr1) To UBound(arr1)
result(i) = arr1(i) + arr2(i)
Next i
AddArrays = result
End Function
If this routine doesn't catch the error, VBA will give other routines above it in the call stack a chance to (See this: VBA Error "Bubble Up"). Here is how a caller might do so:
Public Function addExcelArrays(a1, a2)
On Error Goto EH
addExcelArrays = AddArrays(a1, a2)
Exit Function
EH:
'ERR_VBA_TYPE_MISMATCH isn't defined by VBA, but it's value is 13...
If Err.Number = SOME_BAD_INPUT_CONSTANT Or Err.Number = ERR_VBA_TYPE_MISMATCH Then
'We expected this might happen every so often...
addExcelArrays = CVErr(xlErrValue)
Else
'We don't know what happened...
Call debugAlertUnexpectedError() 'This is something you would have defined
End If
End Function
What "do something meaningful" means depends on the context of your application. In the case of my caller example above, it decides that some errors should be handled by returning an error value that Excel can put in a worksheet cell, while others require a nasty alert. (Here's where the case of VBA within Excel is actually not a bad specific example, because lots of applications make a distinction between internal and external routines, and between exceptions you expect to be able to handle and error conditions that you just want to know about but for which you have no response.)
Don't Forget Assertions
Because you mentioned debugging, it's also worth noting the role of assertions. If you expect AddArrays to only ever be called by routines that have actually created their own arrays or otherwise verified they are using arrays, you might do this:
Function AddArrays(arr1, arr2)
Dim i As Long
Dim result As Variant
Debug.Assert IsArray(arr1)
Debug.Assert IsArray(arr2)
'rest of code...
End Function
A fantastic discussion of the difference between assertions and exceptions is here:
Debug.Assert vs Exception Throwing
I gave an example here:
Is assert evil?
Some VBA Advice About General Array Handling Routines
Finally, as a VBA-specific note, there are VBA variants and arrays come with a number of pitfalls that must be avoided when you're trying to write general library routines. Arrays might have more than one dimension, their elements might be objects or other arrays, their start and end indices might be anything, etc. Here is an example (untested and not trying to be exhaustive) that accounts for some of that:
'NOTE: This has not been tested and isn't necessarily exhaustive! It's just
'an example!
Function addArrays(arr1, arr2)
'Note use of some other library functions you might have...
'* isVect(v) returns True only if v is an array of one and only one
' dimension
'* lengthOfArr(v) returns the size of an array in the first dimension
'* check(condition, errNum) raises an error with Err.Number = errNum if
' condition is False
'Assert stuff that you assume your caller (which is part of your
'application) has already done - i.e. you assume the caller created
'the inputs, or has already dealt with grossly-malformed inputs
Debug.Assert isVect(arr1)
Debug.Assert isVect(arr2)
Debug.Assert lengthOfArr(arr1) = lengthOfArr(arr2)
Debug.Assert lengthOfArr(arr1) > 0
'Account for VBA array index flexibility hell...
ReDim result(1 To lengthOfArr(arr1)) As Double
Dim indResult As Long
Dim ind1 As Long
ind1 = LBound(arr1)
Dim ind2 As Long
ind2 = LBound(arr2)
Dim v1
Dim v2
For indResult = 1 To lengthOfArr(arr1)
'Note implicit coercion of ranges to values. Note that VBA will raise
'an error if an object with no default property is assigned to a
'variant.
v1 = arr1(ind1)
v2 = arr2(ind2)
'Raise errors if we have any non-numbers. (Don't count a string
'with numeric text as a number).
Call check(IsNumeric(v1) And VarType(v1) <> vbString, xlErrValue)
Call check(IsNumeric(v2) And VarType(v2) <> vbString, xlErrValue)
'Now we don't expect this to raise errors.
result(indResult) = v1 + v2
ind1 = ind1 + 1
ind2 = ind2 + 1
Next indResult
addArrays = result
End Function
There's lots of ways to trap errors, some better than others. Alot of it depends on on the nature of the error and how you want to handle it.
1st: In your examples, you aren't handling the basic compiling & runtime errors (see code below).
Function Foobar (Arg1, Arg2)
On Error goto EH
Do stuff
Exit Function
EH:
msgbox "Error" & Err.Description
End Function
2nd: Using the framework example above, you can add all the if-then logical error trapping statements you want & feed it to the EH step. You can even add multiple EH steps if your function is complex enough. Setting things up this way allows you to find the particular function where your logic error occurred.
3rd: In your last example, ending that function as a boolean is not the best method. If you were able to add the 2 arrays, then that function should return the resultant array. If not, it should throw up a msgbox-style error.
4th: I recently started doing a little trick that can be very helpful in some situations. In your VBA Editor, go to Tools->Options->General->Break on ALL errors. This is very helpful when you already have your error handling code in place, but you want to go the exact line where the error occurred and you don't feel like deleting perfectly good code.
Example: Let's say you want to catch an error that wouldn't be caught normally by VBA, i.e. an integer variable should always have a value >2. Somewhere in your code, say If intvar<=2 then goto EH. Then in your EH step, add If intvar<=2 then msgbox "Intvar=" & Intvar.
First, PowerUser gave you a good answer already--this is an expansion on that one.
A trick that I just learned is the "double resume", thus:
Function Foobar (Arg1, Arg2)
On Error goto EH
Do stuff
FuncExit:
Exit Function
EH:
msgbox "Error" & Err.Description
Resume FuncExit
Resume
End Function
What happens here is that in the execution of finished code, your code throws up a MsgBox when an error is encountered, then runs the Exit Function statement & goes on its way (just the same as dropping out the bottom with End Function). However, when you're debugging and you get that MsgBox you instead do a manual Ctrl-Break, then set next statement (Ctrl-F9) to the unadorned Resume and press F8 to step--it goes right back to the line that threw the error. You don't even have to take the extra Resume statements out, since they will never execute without manual intervention.
The other point on which I want to argue (gently) with PowerUser is in the final example. I think it's best to avoid unneeded GoTo statements. A better approach is If intvar<=2 then err.raise SomeCustomNumber. Make sure you use a number that isn't already in use--search 'VB custom error' for more information.