Just double checking: is there any way to ascertain that a string can be converted to an enumeration value that does not involve raising an exception, i.e.:
begin
return Some_Enum'Value ("asdf");
exception
-- Invalid value, do whatever you must.
end;
I don’t think so as 'Valid takes a variable of the type, but I might be missing something?
If the enumeration only has a few values, consider getting the Image of each and comparing them to the string under consideration, after stripping it of extra whitespace and whatnot.
You could try to do a conversion and return true if successful and false on exception, that means you’d also need to do it twice unless you return the enum as an out parameter.
This is one area where an option type would be handy.
You can make a constant indefinite map with a key type of string and element type the enumeration. Then you can call .Contains() with a forced to upper on the input for a Boolean result.
It takes some scaffolding like a custom “&” operator to use to make the constant map and overriding contains to force the to upper but it is not terrible to do. I’ve done it for a compiler keyword search before.
If the enumeration is simple then you just insert the image of the enumeration value as the key
EDIT: Example:
generic
type Enumeration is (<>);
package Enumeration_Search is
package Maps is new Ada.Containers.Indefinite_Hashed_Maps
(Key_Type => String,
Element_Type => Enumeration,
Hash => Ada.Strings.Hash,
Equivalent_Keys => Standard."=");
type Map is new Maps.Map with null record;
type Option(Valid : Boolean := False) is record
case Valid is
when False => null;
when True => Value : Enumeration := Enumeration'First;
end case;
end record;
overriding
function Contains(Self : Map; Key : String) return Boolean;
function Find(Self : Map; Key : String) return Option;
function Make return Map;
function "&"(L : Map; R : Enumeration) return Map;
Empty_Map : constant Map := (Maps.Empty_Map with null record);
end Enumeration_Search;
body file
package body Enumeration_Search is
function Contains(Self : Map; Key : String) return Boolean is
use Ada.Characters.Handling;
begin
return Maps.Map(Self).Contains(To_Upper(Key));
end Contains;
function Find(Self : Map; Key : String) return Option is
use Ada.Characters.Handling;
Location : constant Maps.Cursor := Self.Find(To_Upper(Key));
use type Maps.Cursor;
begin
if Location = Maps.No_Element then
return (Valid => False);
else
return (Valid => True, Value => Self(Location));
end if;
end Find;
function Make return Map is
begin
return Result : Map do
for Value in Enumeration'Range loop
Result.Insert
(Key => Value'Image,
New_Item => Value);
end loop;
end return;
end Make;
function "&"(L : Map; R : Enumeration) return Map is
begin
return Result : Map := L do
Result.Insert
(Key => R'Image,
New_Item => R);
end return;
end "&";
end Enumeration_Search;
example:
type My_Type is (Abc, Def, Ghi);
package My_Type_Search is new Enumeration_Search(My_Type);
use type My_Type_Search.Map;
Full_Search : constant My_Type_Search.Map := My_Type_Search.Make;
Constrained_Search : constant My_Type_Search.Map := My_Type_Search.Empty_Map
& Abc
& Ghi;
Something like that didn’t even occur to me. It seems a little heavyweight for something like this, but if he’s willing to do something like that, he could always use a trie for the mapping. I have a library he can use, if he’d be interested.
Thanks for all the comments, people, and the creativity, but I was hoping there was some simple, ARM-intended way, such a 'Valid attribute that took a string, that I had overlooked.
I think the ARM more often than not tends to go the way of exception propagation for more things than I prefer (but I primarily code in bare metal contexts, so exceptions are less useful to me).
I keep telling people, you can use unconstrained arrays for this…
Let me show you how:
Generic
Type Element is (<>);
Package Generic_Optional is
Subtype Index is Boolean range True..True;
Type Optional is Array(Index range <>) of Element;
Function From_String( Object: String ) return Optional;
End Generic_Optional;
Package Body Generic_Optional is
Function From_String( Object: String ) return Optional is
Begin
Return Optional'( True => Some_Enum'Value (Object) );
Exception
Return Optional'( True..False => <> ); -- Null array
End From_String;
End Generic_Optional;
Granted, you can’t use this technique on unconstrained elements; but otherwise it does confer a big advantage: considering optional as an unconstrained array of 0 or 1 elements guides you to using for-loops on arrays for the processing, allowing you to use other unconstrained arrays [0…N] within the system w/o a [major] rewrite.