delphi delphi-xe anonymous-methods

delphi - Código incorrecto al combinar procedimientos anónimos y anidados



delphi-xe anonymous-methods (2)

¿Cómo puedo saber dónde esto causará problemas?

Es difícil de decir en este punto en el tiempo.
Si supiéramos la naturaleza de la corrección en Delphi XE2, estaríamos en una mejor posición.
Todo lo que puede hacer es abstenerse de usar funciones anónimas.
Delphi ha tenido variables de procedimiento, por lo que la necesidad de tener funciones anónimas listas no es tan grave.
Ver http://www.deltics.co.nz/blog/posts/48 .

Sería interesante para mí saber si esto se soluciona en versiones posteriores de Delphi

Según @Sertac Akyuz esto se ha corregido en XE2.

Personalmente, me desagradan los métodos anónimos y he tenido que prohibir que las personas los usen en mis proyectos Java porque una proporción considerable de nuestra base de códigos estaba en el anonimato (controladores de eventos).
Utilizado con extrema moderación, puedo ver el caso de uso.
Pero en Delphi, donde tenemos variables de procedimiento y procedimientos anidados ... No tanto.

Tengo algunas violaciones de acceso inesperadas para el código Delphi que creo que son correctas, pero parece que no se han compilado correctamente. Puedo reducirlo a

procedure Run(Proc: TProc); begin Proc; end; procedure Test; begin Run( procedure var S: PChar; procedure Nested; begin Run( procedure begin end); S := ''Hello, world!''; end; begin Run( procedure begin S := ''Hello''; end); Nested; ShowMessage(S); end); end;

Lo que sucede para mí es que S := ''Hello, world!'' está almacenando en la ubicación incorrecta. Debido a eso, se ShowMessage(S) una infracción de acceso o ShowMessage(S) muestra "Hola" (y, a veces, se produce una infracción de acceso al liberar los objetos utilizados para implementar procedimientos anónimos).

Estoy usando Delphi XE, todas las actualizaciones instaladas.

¿Cómo puedo saber dónde esto causará problemas? Sé cómo reescribir mi código para evitar procedimientos anónimos, pero tengo problemas para descubrir en qué situaciones conducen al código incorrecto, así que no sé dónde evitarlos.

Sería interesante para mí saber si esto se soluciona en versiones posteriores de Delphi, pero nada más que interesante, la actualización no es una opción en este momento.

En QC, el informe más reciente puedo encontrar el #91876 similar, pero eso se resuelve en Delphi XE.

Actualización :

En base a los comentarios de AlexSC, con una ligera modificación:

... procedure Nested; begin Run( procedure begin S := S; end); S := ''Hello, world!''; end; ...

funciona.

El código de máquina generado para

S := ''Hello, world!'';

en el programa que falla es

ScratchForm.pas.44: S := ''Hello, world!''; 004BD971 B89CD94B00 mov eax,$004bd99c 004BD976 894524 mov [ebp+$24],eax

mientras que la versión correcta es

ScratchForm.pas.45: S := ''Hello, world!''; 004BD981 B8B0D94B00 mov eax,$004bd9b0 004BD986 8B5508 mov edx,[ebp+$08] 004BD989 8B52FC mov edx,[edx-$04] 004BD98C 89420C mov [edx+$0c],eax

El código generado en el programa que falla no ve que S se haya movido a una clase generada por el compilador, [ebp+$24] es Cómo se accede a las variables locales externas de los métodos anidados cómo se accede a las variables locales


Sin ver todo el Código de ensamblador para el todo (Prueba de procedimiento) y solo asumiendo el fragmento que publicó, es probable que en el fragmento de error solo se haya movido un puntero, donde en la versión correcta también se movieron algunos datos.

Entonces, parece que S: = S o S: = '''' causa que el compilador cree una referencia por sí mismo e incluso pueda asignar algo de memoria, lo que explicaría por qué funciona entonces.

También supongo que es por eso que una Violación de Acceso ocurre sin S: = S o S: = '''', porque si no hay Memoria asignada para la Cadena (recuerde que usted solo declaró S: PChar) entonces se genera una Infracción de Acceso porque no se asignó Se accedió a la memoria.

Si simplemente declaras S: String en su lugar, probablemente esto no suceda.

Adiciones después de comentarios extensos:

Un PChar es solo un puntero a la estructura de datos, que debe existir. También otro problema común con PChar es declarar Variables locales y luego pasar un PChar a esa Variable a otros Procs, porque lo que sucede es que la Variable local se libera una vez que la rutina finaliza, pero el PChar aún la señala, lo que aumenta Violaciones de acceso una vez accedidas.

La única posibilidad que existe por Documentación es declarar algo así const S: PChar = ''Hello, world!'' esto funciona porque el compilador puede resolverle un Adresse relativo. Pero esto solo funciona para Constantes y no para Variables como en el Ejemplo anterior. Al hacerlo como en el ejemplo anterior, se debe asignar el almacenamiento para el literal de cadena al que el PChar señala como S:String; P:PChar; S:=''Hello, world!''; P:=PChar(S); S:String; P:PChar; S:=''Hello, world!''; P:=PChar(S); o similar.

Si aún falla al declarar Cadena o Entero, entonces tal vez la Variable desaparezca en algún momento o de repente ya no esté visible en un proceso, pero ese sería otro Problema que no tiene nada que ver con el Problema PChar existente ya explicado.

Conclusión final:

Es posible hacer S:PChar; S:=''Hello, world!'' S:PChar; S:=''Hello, world!'' pero el compilador simplemente lo asigna como una constante local o global como const S: PChar = ''Hello, world!'' hace que se guarde en Ejecutable, un segundo S := ''Hello'' luego crea otro que también se guarda en Ejecutable y así sucesivamente - pero S luego solo apunta al último asignado, todos los demás todavía están en el Ejecutable pero no son accesibles más sin saber la ubicación exacta, porque S solo apunta a la última ubicación asignada.

Así que dependiendo de cuál fue el último S puntos, ya sea Hello, world! o Hello En el ejemplo anterior, solo puedo adivinar cuál fue el último y quién sabe, quizás el compilador solo pueda adivinar y, dependiendo de las optimizaciones y otros factores impredecibles, S podría apuntar repentinamente a Mem no asignada en lugar de a la última por Time Showmessage(S) se ejecuta, lo que provoca una Infracción de acceso.