Получить значение представления типа перечисления в Ada - PullRequest
10 голосов
/ 29 февраля 2012

Мне нужно получить числовое значение, связанное со значением перечисляемого типа в Ada.Не позиция в перечислении, а значение, присвоенное предложению «for TYPE use» каждому значению.

Кто-нибудь знает, возможно ли это?

Ответы [ 6 ]

11 голосов
/ 01 марта 2012

В настоящее время не существует полностью общего решения.Пункты представления перечисления, кажется, разработаны, чтобы затруднить получение этой информации.(Однако Ada 2020 добавит решение; подробности см. В нижней части этого ответа.)

Это:

function Rep is new Ada.Unchecked_Conversion(Enum, Integer);

, вероятно, сработает в большинстве случаев, но есть некоторые серьезные предостережения: значения представления должны находиться в диапазоне Integer'First..Integer'Last, и если размеры Enum и Integer не совпадают, результат фактически определяется реализацией (но он работает с GNAT).

Как говорит Саймон Райт, RM рекомендует Unchecked_Conversion, но это не очень удовлетворительное решение, и определить согласованный тип цели сложно.

Начиная с 2007 года RM рекомендуемый уровень поддержки:

Реализация должна поддерживать как минимум внутренние коды в диапазоне System.Min_Int..System.Max_Int.

, что означает, что преобразование в Integer не всегда достаточно;значение может быть меньше Integer'First или больше Integer'Last.И даже если все значения находятся в этом диапазоне, нет действительно хорошего способа определить целевой тип, который имеет тот же размер, что и тип перечисления.Например, это:

type Enum is (Ten, Twenty, Thirty);
for Enum use (10, 20, 30);
function Rep is new Ada.Unchecked_Conversion(Enum, Integer);

выдает это предупреждение в GNAT:

warning: types for unchecked conversion have different sizes

Но после предупреждения Rep возвращает ожидаемые значения 10, 20 и 30.

RM явно заявляет, что если исходный и целевой размеры в экземпляре Unchecked_Conversion не совпадают, а тип результата скалярный, то

результат функции определяется реализацией,и может иметь недопустимое представление

Таким образом, тот факт, что вышеупомянутое работает для GNAT, не означает, что он гарантированно будет работать везде.

Для реализации, которая только поддерживает значения в диапазоне System.Min_Int..System.Max_Int, вы можете сделать что-то вроде этого:

type Enum is (...);
for Enum use (...);
type Longest_Integer is range System.Min_Int .. System.Max_Int;
function Rep is new Ada.Unchecked_Conversion(Enum, Longest_Integer);

и игнорировать предупреждение.Но компиляторам разрешено принимать значения, превышающие System.Max_Int, если они находятся в диапазоне некоторого целочисленного типа.Например, GNAT отклоняет это, но другой компилятор Ada может принять это:

type Longest_Unsigned is mod System.Max_Binary_Modulus;
type Unsigned_Enum is (Zero, Huge);
for Unsigned_Enum use (0, Longest_Unsigned'Last);

и преобразование Unchecked_Conversion из этого в любой тип целого числа со знаком не будет работать.И у вас все еще есть потенциальная проблема реализации определенных результатов, если размеры не совпадают.

Вот общее решение, которое должно работать для любого типа перечисления , если (a) значения представленияв диапазоне System.Min_Int..System.Max_Int, и (b) если реализация Unchecked_Conversion ведет себя лучше, чем стандарт Ada требует, чтобы она была:

type Longest_Signed is range System.Min_Int .. System.Max_Int;

generic
    type Enum is (<>);
function Generic_Rep(E: Enum) return Longest_Signed;

function Generic_Rep(E: Enum) return Longest_Signed is
    function Rep is new Ada.Unchecked_Conversion(Enum, Longest_Signed);
begin
    return Rep(E);
end Generic_Rep;

Учитывая все это заблуждение, вы можете рассмотреть возможность использования некоторого механизмакроме предложений представления перечисления, чтобы делать то, что вы пытаетесь сделать.

ОБНОВЛЕНИЕ:

GNAT имеет атрибуты, определенные реализацией 'Enum_Rep и 'Enum_Val.Ожидается, что Ada 2020 примет их.

http://www.ada -auth.org / standard / 2xrm / html / RM-13-4.html # p10.1

5 голосов
/ 29 февраля 2012

Если вы используете GNAT и не возражаете против использования компилятора, этот компилятор предоставляет атрибут Enum_Rep для этой цели.

2 голосов
/ 29 февраля 2012

AARM 13.4 (пункт 11/1) рекомендует Unchecked_Conversion (вероятно, целое число).

1 голос
/ 24 августа 2015

То, что я нашел для работы в GNAT, предназначено для:

type MyEnum is (A, B, C);

Я должен был сделать:

EVal : MyEnum := B;
IVal : Integer := MyEnum'Enum_Rep(EVal);
1 голос
/ 16 августа 2013

Поскольку я понимаю руководство по качеству и стилю, вообще не следует заботиться о внутренних значениях представлений перечислений. после долгих исследований я однажды решил использовать следующую конструкцию:

type enum_c is (clk_eq, clk_div_2, clk_div_16, clk_div_128, clk_div_1024);

type enum_c_values is array (enum_c) of natural; -- or any type you wish

cdiv_values : constant enum_c_values := (
   clk_eq         =>    1,
   clk_div_2      =>    2,
   clk_div_16     =>   16,
   clk_div_128    =>  128,
   clk_div_1024   => 1024 );

c : enum_c := clk_div_128;
...
put_line("c =" & c'img & " natural value associated w/ c =" & cdiv_values(c)'img);
1 голос
/ 29 февраля 2012

Если вы не используете компиляторы JVM или .NET, вы можете наложить их два;что-то вроде:

Value : Integer;
For Value'Address use ENUM_VAR'Address;

Вы захотите использовать прагму, которая подавляет инициализацию, хотя я сейчас ее не вспоминаю.

IIRC, есть также вариант-Метод записи, в котором можно точно наложить поля и использовать запись в качестве своего рода преобразования представления.

...