I have just started using Ada, and I'm finding the generic package declarations to be rather silly. Maybe I'm not doing it right, so I'm looking for better options.
Take a look at the below example.
package STD_Code_Maps is new
Ada.Containers.Map(Key_Type => STD_Code_Type;
Element_Type => Ada.Strings.Unbounded.Unbounded_String);
STD_Code_Map : STD_Code_Maps.Map;
-- . . .
procedure Do_Something is
Item : Ada.Strings.Unbounded.Unbounded_String;
begin
Item := STD_Code_Maps.Element(STD_Code_Maps.First(STD_Code_Map));
-- Do something with the Item
-- . . .
end Do_Something;
It would be much cleaner to simply be able to write STD_Code_Map.First.Element instead of the godforsaken STD_Code_Maps.Element(STD_Code_Maps.First(STD_Code_Map));
Obviously I'm doing this wrong -- I think. I'm repeating the phrase STD_Code_Map at least thrice over there. I'm all for verbosity and everything, but really the code I'm writing seems bad and silly to me.
I was wondering if there is solution that doesn't require you to rename the package to something like package Map renames STD_Code_Maps; which would of course shorten the code but I don't want to do this on each and every procedure entry. I really think something like STD_Code_Map.First.Element would be much simpler. Can this be done in Ada 2012?
Note: Using the Unbounded_String package by default is also so difficult. Did the standard library designers actually give much thought to the ridiculous and overly long package hierarchy?
Thanks for reading this, and potentially helping me out. I'm new to Ada.
GNAT GPL 2012 and 2013, and FSF GCC 4.7 and 4.8, support the new container indexing scheme of Ada 2012, which means that you can write
Item := STD_Code_Map ({some Cursor});
And you can do this even with the -gnat05 switch to force Ada 2005 mode! (which has to be a bug).
Ada 2005 allows you to call a primitive function of a tagged type using object.function notation, provided that the first operand is of the tagged type; so you can write STD_Code_Map.First as shorthand for STD_Code_Maps.First (STD_Code_Map).
Putting these together, you can write
Item := STD_Code_Map (STD_Code_Map.First);
which is quite short!
The issue has nothing to do with generics. As Simon pointed out, you can say STD_Code_Map.First, since the type of STD_Code_Map is a tagged type and Ada supports this notation for tagged types. On the other hand, the type of STD_Code_Map.First is a Cursor type, which isn't tagged (making it tagged would have caused problems declaring certain operations that take both a Cursor and a Map). But even without the Ada 2012 container indexing that Simon mentioned, you can say
STD_Code_Maps.Element(STD_Code_Map.First);
which is a little better. Besides renaming a package, you can also rename the function:
function Elem (Position : STD_Code_Maps.Cursor) return STD_Code_Maps.Element_Type
renames STD_Code_Maps.Element;
and you can now use just Elem instead of STD_Code_Maps.Element wherever the renaming is directly visible. (You could call it Element if you want to. The renaming name can be the same or it can be different.) This could be helpful if you use that function a lot.
Ada was designed for readability and maintainability. (written once, read & maintained for much longer) This means that it does get a little verbose at times. If you prefer terse & cryptic there are plenty of other languages out there !
If you want to avoid typing STD_Code_Map all the time, just use a use clause:
use STD_Code_Map;
which would mean your code of
Item := STD_Code_Maps.Element(STD_Code_Maps.First(STD_Code_Map));
would become
Item := Element(First(STD_Code_Map));
Getting names nice and readable in Ada can sometimes be tricky. Often times language designers made the task worse than it had to be, by designing Ada standard library packages for use with Ada use clauses, without a thought to how they'd look to some poor sap who either can't or doesn't want to use that feature.
In this case though, there are things you can do on your own end.
For example "_Maps.Map" is redundant, so why not get rid of it from the package name? Why not use names so that you can write:
package STD_Code is new
Ada.Containers.Map(Key_Type => STD_Code_Type;
Element_Type => Ada.Strings.Unbounded.Unbounded_String);
Map : STD_Code.Map;
-- . . .
procedure Do_Something is
Item : Ada.Strings.Unbounded.Unbounded_String;
begin
Item := STD_Code.Element(STD_Code.First(Map));
-- Do something with the Item
-- . . .
end Do_Something;
Now Code looks like a bit of a null-meaning word too. Everything in a program is a code. So I'd consider ditching it as well. Unsually I name my Ada container packages something that says their basic theoretical function (eg: STD_to_String), while objects are more specific nouns.
Also, I should point out that if your map is constant and you can live with names that look like identifiers, often times you can get rid of maps to strings entirely by using an enumerated type and the 'image attribute.
If you're using Ada 2012 the STD_Code_Maps.Element(STD_Code_Maps.First(STD_Code_Map)); could become:
Function Get_First( Map : STD_Code_Maps.Map ) return Unbounded_String is
( STD_Code_Maps.Element(Map.First) );
That is an example of the new Expression-functions, which were mainly introduced for pre- and post-conditions.
Of course, if you're using Ada 2012 then the precise map you'd likely want is Ada.Containers.Indefinite_Ordered_Maps -- the Indefinite_* containers are those that [can] have indefinite elements, like String.
In addition to TED's comment, using an enumeration when the maps are constant have even more advantages, namely case coverage: the compiler will flag as an error any case-statement that doesn't cover all alternatives. So if you add a new code it will flag all the case-statements that you need to modify. (Of course this benefit is lost when you use the others case.)
Related
Posting for two reasons: (1) I was stuck on unhelpful compiler errors for far too long for such a simple issue and I want the next person to google those messages to come upon my (or other) answers, and (2) I still don't understand disallowing a use clause, so my own answer is really incomplete.
In order to call a program in two places with mostly the same arguments, I want to use the '&' to append to a default list inline:
declare
Exit_Code : constant Integer := GNAT.OS_Lib.Spawn (Program_Name => "gprbuild", Args => (Default_GPR_Arguments & new String'(File_Name_Parameter)));
begin
if Exit_Code /= 0 then
raise Program_Error with "Exit code:" & Exit_Code'Image;
end if;
end;
However, the compiler complains that System.Strings.String_List needs a use clause:
operator for type "System.Strings.String_List" is not directly visible
use clause would make operation legal
But inserting use System.Strings.String_List yields:
"System.Strings.String_List" is not allowed in a use clause
I also got this warning:
warning: "System.Strings" is an internal GNAT unit
warning: use "GNAT.Strings" instead
So I substituted GNAT for System in the with and the use clause and got an extra error in addition to the original 'you need a use clause for System.Strings.String_List' one:
"GNAT.Strings.String_List" is not allowed in a use clause
Why is GNAT.Strings.String_List not allowed in a use clause? Section 8.5 on use clauses doesn't seem to state anything on disallowed packages, so is this a compiler bug? Is it possible to define a new package that cannot have a use clause?
In a use clause of the form
use Name;
Name must be a package name. GNAT.Strings.String_List is a subtype name, not a package name.
There are a number of ways to invoke "&" for String_List. The simplest is to use the full name:
GNAT.Strings."&" (Left, Right)
but presumably you want to be able to use it as an operator in infix notation, Left & Right. Ways to achieve this, in decreasing specificity:
function "&" (Left : GNAT.Strings.String_List; Right : GNAT.Strings.String_List) return GNAT.Strings.String_List renames GNAT.Strings."&"; This makes this specific function directly visible.
use type GNAT.Strings.String_List; This makes all primitive operators of the type directly visible.
use all type GNAT.Strings.String_List; This makes all primitive operations of the type (including non-operator operations) directly visible.
use GNAT.Strings; This makes everything in the package directly visible.
Looks like it is a design decision. And many other packages in System follows this rule. From the s-string.ads (package specification for System.String):
-- Note: this package is in the System hierarchy so that it can be directly
-- be used by other predefined packages. User access to this package is via
-- a renaming of this package in GNAT.String (file g-string.ads).
My guess why this is done in that way: because it isn't in the Ada specification, but extension from GNAT.
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.
This seems like a really basic question, but can't find the answer. How do I create a collection in Julia? For example, I want to open a text file and parse each line to create an (iterable or otherwise) collection. Obviously I don't know how many elements there are in advance.
I can iterate through the lines like this
I = each_line(open(fileName,"r"))
state = start(I)
while !done(I, state)
(i, state) = next(I, state)
println(i)
end
But I don't know how to put each i into an array or other collection. I tried
map( i -> println(i), each_line(open(fileName,"r") ) )
But got the error
no method map(Function,EachLine)
You could do this:
lines = String[]
for line in each_line(open(fileName))
push!(lines, line)
end
And then lines contains the list of lines. You need the String in the first line to make the array extensible.
Standard collections and supported operations are mainly covered in the standard library documentation.
Specifically, the Deques section covers all of the operations supported by the 1d Array type (vector), including push! and pop! as well as insertion, resizing, etc.
Omar's answer is correct, and I will just add a small qualification: String[] creates a 1d array of Strings. The same constructor syntax may be used for example to create Int[], Float[], or even Any[] vectors. The latter type may hold objects of any type.
Depending on your Julia version, you may also be able to write collect(eachline(open("LICENSE.md"))) or [eachline(open("LICENSE.md"))...]. I think these won't work in 0.1.x versions but will working in newer 0.2 development versions (which are recommended at this point – 0.2 is on its way soon).
What is a purpose in Pascal to declare variable that is pointer to a pointer? I have a code in Mac Think Pascal. Here is some parts from the code that I don't understand:
type
my_array = array[1..100] of integer;
my_array_pointer = ^my_array;
my_array_handle = ^my_array_pointer;
...
var
xx : my_array_handle;
...
begin
xx:= my_array_handle(NewHandle( sizeof(my_array)) );
As you see, the last line is an assignment of a type my_array_handle to variable xx. What does it mean? What does NewHandle function do? (This is an internal function of Think Pascal). Actually, I need to convert a Think Pascal program to Windows Pascal. However I cannot find the description of NewHandle function, and don't know how to implement this function using the standard (New(), GetMem() etc) pointer functions.
This is a classic macos feature, not typically something of Pascal.
I don't know exactly, but it had something to do with relocatability of the loaded program in a non PM environment.
Note that the indirect pointer is allocated via an OS function, which probably means that it is allocated in some table that is maintained by the OS. (so that the OS can move/relocate the program?)
In modern Mac (and other) programming this whole principle is alien. Just clean it up.
(added)
If you want to keep these redirections, you could try your luck with something like:
function newhandle( nrbytes:integer):ppointer;
var xx : ppointer;
begin
new(xx);
getmem(xx^,nrbytes);
newhandle:=xx;
end;
I didn't add this originally, but I recommend you simply clean up these anachronistic indirect references and use my_array_pointer based pointers and getmem or new directly.
The meaning of these indirect references has no use on non m68k Classic Mac OS systems, though afaik later PPC versions still somewhat support them. (PPC is always protected mode)
If you really want the gritty details, you probably want to subscribe to the mac-pascal list.
My Taglist in a C code:
macro
|| MIN_LEN
|| MAX_ITERATIONS
||- typedef
|| cell
|| source_cell
||- variable
|| len_given
Taglist elements (domain):
A = {MIN_LEN, MAX_ITERATIONS, cell, source_cell, len_given}
Code snippets (codomain):
B = {"code_MIN_LEN", "code_MAX_ITERATIONS", ..., "code_len_given"}
Goal: to have bijection between the sets A and B.
Example: I want to remove any element in A, such as the MIN_LEN, from A and B by removing either its element in A or B.
Question: Is there a way to quarantee a bijection between A and B so that a change either in A or in B results in a change in the other set?
I strongly doubt you can do that. The taglist plugin uses ctags to collect the symbols in your code and display them in a lateral split. The lateral split contains readonly information (if you try to work on that window, vim tells you that modifiable is off for that buffer).
What you want to achieve would imply quite complex parsing of the source code you are modifying. Even a simple task like automatic renaming (assuming you modify a function name entry in the taglist buffer and all the instances in your source are updated) requires pretty complex parsing, which is beyond the ctags features or taglist itself. Deleting and keeping everything in sync with a bijective relationship is even more complex. Suppose you have a printf line where you use a macro you want to remove. What should happen to that line? should the whole line disappear, or just the macro (in that case, the line will probably be syntactically incorrect.
taglist is a nice plugin for browsing your code, but it's unsuited for automatic refactoring (which is what you want to achieve).
Edit: as for the computational complexity, well, the worst case scenario is that you have to scout the whole document at every keystroke, looking for new occurrence of labels that could be integrated, so in this sense you could say it's O(n) at each keystroke. This is of course overkill and the worst method to implement it. I am not aware of the computational complexity of the syntax highlight in vim, (which would be useful to extract tags as well, via proper tokenization), but I would estimate it very low, and very limited in the amount of parsed data (you are unlikely to have large constructs to parse to extract the token and understand its context). In any case, this is not how taglist works. Taglist runs ctags at every vim invocation, it does not parse the document live while you type. This is however done by Eclipse, XCode and KDevelop for example, which also provide tools for automatic or semiautomatic refactoring, and can eventually integrate vim as an editor. If you need these features, you are definitely using the wrong tool.