2011-06-06 21 views
7

¿Cómo se organiza un set en memoria en Delphi?Diseño de memoria de un conjunto

Lo que trato de hacer es lanzar un tipo simple a un tipo de conjunto como

var 
    MyNumber : Word; 
    ShiftState : TShiftState; 
begin 
    MyNumber:=42; 
    ShiftState:=TShiftState(MyNumber); 
end; 

Delphi (2009) no permitirá esto y no entiendo por qué. Haría mi vida mucho más fácil en los casos en que obtenga un número en el que los bits individuales codifiquen diferentes valores enum y simplemente podría lanzarlo así. Se puede hacer esto?

Un enfoque que iba a ir para es:

var 
    ShiftState : TShiftState; 
    MyNumber : Word absolute ShiftState; 
begin 
    MyNumber:=42; 
end; 

Pero antes de hacer esto pensé que pediría el diseño de memoria. Es más un sentimiento que un conocimiento lo que estoy teniendo ahora sobre esto.

Respuesta

6

Un conjunto Delphi es un campo de bit cuyos bits corresponden a los valores asociados de los elementos en su conjunto. Para un conjunto de tipos enumerados normales, el diseño de bit es directa:

  • bit 0 corresponde a establecer elemento con el valor ordinal 0
  • bit 1 corresponde a establecer elemento con el valor ordinal 1
  • etc. .

Las cosas se ponen un poco interesantes cuando se trata de un conjunto no contiguo o con un conjunto que no comienza en 0.Puede hacerlo utilizando el tipo subrango de Delphi (ejemplo: set of 3..7) o el uso de los tipos enumerados que especifican el valor ordinal real de los elementos:

type enum=(seven=7, eight=8, eleven=11); 
EnumSet = set of enum; 

En tales casos Delphi asignará la cantidad mínima requerida de bytes que incluirá todos bits requeridos, pero no "cambiará" los valores de bit para usar menos espacio. En el ejemplo EnumSet Delphi utilizará dos bytes:

  • El primer byte tendrá su séptimo bit asociado con seven
  • El segundo byte tendrá bit 0 asociado con eight
  • El segundo byte tendrá bit 3 asociado con eleven

Se puede ver algunas pruebas que hice aquí: Delphi 2009 - Bug? Adding supposedly invalid values to a set

Las pruebas se realizaron usando Delphi 2010, no las repitieron para Delphi XE.

+0

Cosmin, muchas gracias por resumirlo! ¡También disfruté mucho leyendo sobre tus pruebas en la publicación vinculada y recomiendo a todos que lo lean! Ahora todo está claro y yo y el compilador estamos contentos :) –

1

Tienes que elegir un tipo ordinal de tamaño correcto. Para mí (D2007) el código se compila con MyNumber: Byte:

procedure Test; 
var 
    MyNumber: Byte; 
    ShiftState: TShiftState; 
begin 
    MyNumber := 42; 
    ShiftState := TShiftState(MyNumber); 
end; 

He utilizado esta técnica en algunas situaciones y no tener problemas.


ACTUALIZACIÓN

El tipo TShiftState se ha extendido desde Delphi 2010 para incluir dos nuevos estados, ssTouch y ssPen, basados ​​en el corresponding doc page (current doc page). El Delphi 2009 doc todavía tiene TShiftState definido como un conjunto de 7 estados.

lo tanto, su intento de convertir a WordTShiftState funcionaría en Delphi 2010+, pero Byte es el tamaño adecuado para Delphi 2009.

+0

¡Gracias por este empujón en la dirección correcta! Busqué más y encontré mi respuesta en otro hilo. Por cierto: en Delphi XE, el 'TShiftState' contiene 9 miembros, por lo que creo que tendríamos que convertir' MyNumber' en 'Word', no en' Byte'. –

+0

Gracias por la edición, @Andriy! –

+0

@Heinrich: Mantuve esta página abierta sin actualizarla demasiado tiempo y no vi su comentario. Entonces parece que encontraste la explicación antes que yo. Aún así, creo que mi actualización no dañará la respuesta de Ulrich. :) –

1

Por desgracia, en este momento nos topamos con la siguiente pregunta: Delphi 2009 - Bug? Adding supposedly invalid values to a set

El La respuesta aceptada de Cosmin contiene una descripción muy detallada de lo que está sucediendo con los sets en Delphi. Y por qué es mejor no utilizar mi enfoque con absolute. Aparentemente, una variable establecida puede tomar de 1 a 32 bytes de memoria, dependiendo de los valores enum.

+0

Deberías usar algo como {$ IF SizeOf (set_in_question) <> SizeOf (typecast_var)} {$ ERROR DE MENSAJE} –

0

utilizo este:

Para < = 8 elementos, PBYTE (@MyNumber) ^, por < = 16 elementos, pword (@MyNumber) ^, etc.

Si el enum ocupa más espacio (a través de la opción del compilador de tamaño mínimo) esto todavía funcionará.

+0

Me gustaría ir a 'Byte (MyEnumVar)' etc y tener los controles del compilador agregados. Uno puede escribir un par de funciones 'OrdToMyEnum' y volver para centralizar los moldes. Por lo tanto, solo tiene un lugar para cambiar si el tipo de enumeración debe aumentar al tamaño "siguiente". –

Cuestiones relacionadas