Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Otros entornos y lenguajes > Lazarus, FreePascal, Kylix, etc.
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 27-05-2015
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: jul 2004
Ubicación: Salta, Argentina
Posts: 5.582
Poder: 25
Delphius Va camino a la fama
Una correcta manera de utilizar TFileStream capturando posibles excepciones variadas?

Pues, eso... como dice el título. ¿Cuál es la manera más apropiada de manejar un TFileStream capturando y lanzando las excepciones de forma apropiada?

Actualmente tengo esta chapuza de código:


Código Delphi [-]
procedure TArrayConverter.LoadMatrix(AMatrix: TAMatrix; FileName: string);
var Header: TMatrixHeader;
    Fmt: TFileFormat;
    i, j, RM, CM, Err, ConvE: integer;
    Can: boolean;
begin
  Can := CheckMatrix(AMatrix, RM, CM, Err);
  if Can AND (Err = OPERATION_DONE)
     then begin
            try
              // Cargar el archivo
              FFile.Create(FileName, fmOpenRead or fmShareDenyWrite);
              // leemos formato
              FInUse := true;
              Fmt.SizeFile := FFile.Seek(0, soEnd);
              FFile.ReadBuffer(Fmt.IDIni, SizeOf(Fmt.IDIni)); // ID.Ini
              FFile.ReadBuffer(Header, SizeOf(Header));       // Header
              FFile.Seek(INI_ID_END, soFromEnd);
              FFile.ReadBuffer(Fmt.IDEnd, SizeOf(Fmt.IDEnd)); // ID.End
              // ¿Valido?
              if ISValidFormat(Fmt, Header)
                 then begin
                        // Supuesto tamaño de datos
                        if (Header.Rows = RM) AND (Header.Cols = CM)
                           then begin
                                  // Leemos data
                                  if Header.Orientation = aoCol
                                     then begin
                                            // ... por columnas
                                            for j := 0 to CM - 1 do
                                              for i := 0 to RM - 1 do
                                                FFile.ReadBuffer(AMatrix[i, j], SizeOf(TYPEDATA));
                                          end
                                     else begin
                                            // ... por filas
                                            for i := 0 to RM - 1 do
                                              for j := 0 to CM - 1 do
                                                FFile.ReadBuffer(AMatrix[i, j], SizeOf(TYPEDATA));
                                          end;
                                end
                           else ConvE := 3;
                      end
                 else ConvE := 2;
            finally
              FFile.Free;
              FInUse := false;
            end;
          end
     else ConvE := 1;
  // ¿Lanzamos excepción?
  case ConvE of
  1: raise EInconsistentArray.Create('Check of matrix failed', itCheck);
  2: raise EFileOperation.Create('Invalid matrix file format');
  3: raise EInconsistentArray.Create('The dimensions of the matrix and file data do not match',
     itDimNotMatch);
  end;
end;


Creería que el código se explica solo. Básicamente estoy leyendo un archivo matricial y cargando la data. Mi archivo está pensado con una estructura como la siguiente:

1) Viene una especie de identificador de archivo. Para el caso de matrices, tiene un valor y para vectores otro.
2) Seguidamente una cabecera que contiene la información relacionada con la dimensión de la estructura de datos.
3) Los datos propiamente dichos
4) Y por último un identificador de fin de archivo. Este es común para ambos tipos de archivos.


Se que puede ser mejorado. Pero ya tengo la cabeza muy hecha trizas, y se me confunden los tantos... Por un lado quisiera poder detectar posibles excepciones que arroje el FFileStream, desde su creación hasta cuando intenta leer y/o escribir y por el otro quisiera poder lanzar las excepciones propias que tengo definidas para el contexto de esta clase que estoy diseñando en base a mis pruebas. ¿Cómo debo proceder? ¿Un doble try anidado? ¿Cómo lo encararían ustedes?

Por si ayuda a comprender el código, adjunto la descripción de la clase:


Código Delphi [-]
  TArrayConverter = class
    private
      FFile: TFileStream;
      FInUse: boolean;
      function IsValidFormat(AFormat: TFileFormat; AHeader: TMatrixHeader): boolean; overload;
      function ISValidFormat(AFormat: TFileFormat; AHeader: TVectorHeader): boolean; overload;
      function MakeHeader(Rows, Cols: integer; Orientation: TArrayOrientation): TMatrixHeader;
        overload;
      function MakeHeader(Dim: integer): TVectorHeader; overload;
    public
      constructor Create;
      destructor Destroy; override;
      procedure LoadMatrix(AMatrix: TAMatrix; FileName: string);
      procedure LoadVector(AVector: TAVector; FileName: string);
      procedure SaveMatrix(AMatrix: TAMatrix; OnDir: TArrayOrientation; FileName: string);
      procedure SaveVector(AVector: TAVector; FileName: string);
      property InUse: boolean read FInUse;
  end;

Los métodos MakeHeader() como IsValidFormat() creerían que no hace falta que adjunte. Se pueden hacer una idea de su uso. Básicamente se procede a armar la cabecera de cada archivo, y en los otros de realizar las comprobaciones de formato para asegurarse de que se ha leído un archivo correcto.

Leyendo la documentación, durante la creación del TFileStream es posible que se presente una excepción EFOpenError, y también puede presentarse excepciones durante el ReadBuffer y en el WriteBuffer... no dice la documentación cual. Aparentemente, de lo que estoy viendo en el código de la clase TStream parecen ser EReadError y EWriteError respectivamente.

Para el guardado de vectores el código sería algo similar. Las versiones Save() estimo que debieran de tener un código análogo, salvando el detalle de aplicar un WriteBuffer.

Cualquier ayuda y propuesta se les agradece.


De algunas búsquedas que he estado realizano he notado que todos los códigos de ejemplo no protegen la creación del TFileStream dentro de un try, si en cambio asumen un código exitoso y proceden a aplicar un try-finally para asi liberar el TFileStream. Es decir:


Código Delphi [-]
FS := TFileStream.Create(...);
try
  // Usar el FS
finally
  FS.Free;
end;


Naturalmente, cuando se puede dar garantías de que el modo de apertura o creación del archivo nos permite un código seguro para una lectura o bien de la escritura no hay problemas con el código anterior (o al menos no esperaría algo tan problemático). Sobre todo si uno tiene separado lo que hace escritura de la lectura.
Me he estado cuestionando, ¿Y si espero que mis archivos sean de lectura Y escritura? ¿Que acaso no vale la pena proteger la creación del TFileStream?


Yo ya estoy medio perdido.

Recuerdo la discusión debate que hubo en ClubDdelphi sobre algo relacionado con esto.

En él se comentó sobre el caso particular que plantea justamente: para el caso en donde un constructor eleva una excepción. Para ese entonces, el debate no hiló fino en ese caso y se abordó hacia un planteo general. La documentación de Delphi sugiere que un objeto es creado o no creado. Nada de a medias, que se invoca a Destroy implícitamente y no tendría sentido un Free por el lado de Finally.
Más me sigo cuestionando, ¿Y que hacemos como excepción?
Tu dices que debiera de controlarse efectivamente de que el recurso, en este caso, un archivo esté abierto.

Sabemos que entre Delphi y Lazarus tienen sus diferencias. ¿Aquí será un caso? Intenté llegar a algo que me aclare el punto, y llego a algo parecido a la discusión en CD pero para el caso de Lazarus, no hay una confirmación real si FPC se comporta igual que Delphi en esto. La gente que ha intervenido en el mailing list asume que SI.

¿Debiera de proteger con except? ¿con Finally? ¿emplear doble try como se ejemplifica acá?

Yo ya estoy confundido.

Suelo tener cuidado con los constructores y destructores. Me he valido varias veces del uso de excepciones, pero el emplear el TFileStream y el querer poder tanto capturar posibles excepciones de éste, como de lanzar algunas propias para el contexto de mi clase TConverter y que en última las clases que usen a ésta sepan valerse me ha vuelto a las bases para replantear estas cuestiones.


Muchas gracias.
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #2  
Antiguo 27-05-2015
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.101
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
Pues yo no sabría responder con seguridad tu duda, así que mejor no contesto porque no quiero liarte más, a ver si alguien experto en el tema es capaz de iluminarnos.

Lo que sí quiero es saludarte, que hacía tiempo que no te veía, espero que todo te vaya bien

Última edición por Casimiro Notevi fecha: 27-05-2015 a las 23:09:54.
Responder Con Cita
  #3  
Antiguo 27-05-2015
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: jul 2004
Ubicación: Salta, Argentina
Posts: 5.582
Poder: 25
Delphius Va camino a la fama
Cita:
Empezado por Casimiro Notevi Ver Mensaje
Pues yo no sabría responder con seguridad tu duda, así que mejor no contesto porque no quiero no liarte más, a ver si alguien experto en el tema es capaz de iluminarnos.

Lo que sí quiero es saludarte, que hacía tiempo que no te veía, espero que todo te vaya bien
Casimiro, me extraña de vos. Se que eres un maestro y sabes un montón. Asi que no me vengas con lo de hacerse de menos.
Cualquier aporte ayudará a este pobre loco.

Gracias por el saludo. Se agradece muchísimo. Lamentablemente debo informar que mi situación personal no es del todo buena. Estoy con un pié en la calle; prácticamente sin plata en mis bolsillos, y las oportunidades comerciales y profesionales no son muy alentadoras.
Tengo que confesarte que desde que empecé mis estudios universitarios quien debían en teoría debe dar apoyo lo único que ha hecho fue acortarme las posibilidades de desarrollarme profesionalmente y constantemente ha estado cerrando las puertas que yo por mis medios estuve tratando de abrilas. Las situación ya era tan insorportable que para evitar un mal mayor, tuve que abandonar mi lugar de residencia y buscar hospedaje en otros lugares. Ya debo volver en unos días y estoy seriamente convencido de iniciar acciones legales contra esta persona de no lograr que desaparezca y me deje tranquilo y al resto. Demasiado daño ha causado al círculo familiar. En realidad son 16 años de tortura, pero desde que cumplí los 18 se ha vuelto peor y no dejan de llegarme comentarios de los desmadres que ocasiona a terceros. Basta con decir que algunos de mis amigos de Facultad no han querido pisar más mi casa para no tener problemas con él; e incluso una psicóloga conocida me ha sugerido que lo deberíamos de internar bajo tratamiento psiquiátrico. Intenté que las cosas mejoraran por las buenas, y mucho me aguanté y tragué pero veo que tendrán que ser por las malas.
Actualmente estoy tratando de terminar unos proyectos, y quisiera ver si de una vez logro concretar que se cierre algunas ideas comerciales que vengo pensando y me han sugerido mis amigos que me quedan en contacto y tengo más a mano. Lamentablemente por el tipo de formación que he recibido y los gustos y aficciones a ciertos temas que por acá pocos manejan no hay mucho mercado. No queda otra que jugar a la Oferta para generar Demanda.

Bueno, mejor volvamos el hilo a su cauce normal.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #4  
Antiguo 27-05-2015
Avatar de mamcx
mamcx mamcx is offline
Moderador
 
Registrado: sep 2004
Ubicación: Medellín - Colombia
Posts: 3.918
Poder: 25
mamcx Tiene un aura espectacularmamcx Tiene un aura espectacularmamcx Tiene un aura espectacular
He estado aprendiendo mucho últimamente sobre programación funcional, en especial con F#.

Este articulo lo recomiendo muchísimo:

http://fsharpforfunandprofit.com/fppatterns/

Pongo esto porque conceptualmente aclara muchas cosas, lo que es mas "difícil" es aplicarlo a un lenguaje OO. Afortunadamente, Delphi es lo suficiente flexible y resulta mas simple que con Java!

Ahora voy a varios puntos:
---

Cita:
he notado que todos los códigos de ejemplo no protegen la creación de
Lo cual es correcto, tanto conceptual como técnicamente. En vez de irme por las ramas, un concepto muy practico que viene del lenguaje funcional Erlang es:

"Fallar de inmediato"
https://www.sics.se/~joe/thesis/arms...hesis_2003.pdf

(Secciones 4.3 / 4.4).

En resumen? La forma correcta de hacer programas robustos es no hacer programación "primariamente" defensiva, sino permitir fallar rápido, y tener un "supervisor" que se encargue de aplicar una política de recuperación.

Cita:
Error handling in Erlang is radically diferent to error handing in most
other programming languages. The Erlang philosophy for handling errors
can be expressed in a number of slogans:

• Let some other process do the error recovery.
• If you can’t do what you want to do, die.
• Let it crash.
• Do not program defensively.
Que es casi exactamente lo que sucede aquí. Si la creación del stream fracasa, NO TIENE SENTIDO SEGUIR ADELANTE. Es responsabilidad de su "supervisor" (ie: Quien llamo al método donde se intento crear el stream) determinar que hacer (reintentar? Abortar todo el sistema? Notificar y esperar un tiempo? etc).

Esto significa que solo te debes preocupar por resolver el problema a mano, no todos los posibles. Intentar aplicar una programación excesivamente defensiva es un anti-patron, que lleva a tonterías como:

Código Delphi [-]
try
//Alguna cosa
except:
  //No hacer nada o suponer que el error fue de permisos insuficientes!
end;

Es curioso, porque el uso de excepciones es una forma simplista de aplicar lo que dicen en Erlang, el problema es que la gente no le pone cuidado a lo que lees. Como es? "EXCEPCIONES", solo, "EXCEPCIONAL!". Una vez manejado (si acaso) lo excepcional, debe andar en la "ruta feliz" donde se asume que todo ira bien.

Te preguntaras porque arranco por aquí. Es simplemente porque quiero que solo te preocupes por "resolver el problema que tienes a mano", lo cual, lleva a hacer funciones/métodos cortos y faciles de testear.

Esa es la tesis del creado de Erlang. Y Erlang es famoso por ser el lenguaje/runtime mas robusto de todos, asi que tienen de donde saber porque es mejor asi

--

La segunda tesis importante que se aprende con programación funcional, es que el "manejo de estado" es una de las principales razones de la complejidad no esencial en el desarrollo de software. Eso se nota en tu código con el asunto de la variable ConvE.

La pregunta es: Como hago el código, que siga el modelo de la computacion I/O: Entrada -> Proceso -> Salida, con el minimo de dependencias y estado no esencial?

---

Ahora como lo aplicaria yo (no testee el codigo, solo lo reformatee), sin desviarme mucho del estilo que tienes:

Código Delphi [-]

function LoadFile(FileName: string):TFileStream;
begin
    Result := FFile.Create(FileName, fmOpenRead or fmShareDenyWrite);
    //FInUse := true; Redundante: El OS ya hace esto
    //Hasta
    FFile.ReadBuffer(Fmt.IDEnd, SizeOf(Fmt.IDEnd)); // ID.End
end;

procedure ReadMatrix(AMatrix: TAMatrix; FFile: TFileStream; RM, CM:Int);
begin
    // Mover los problemas mas cerca de donde se detectan!
    if not(ISValidFormat(Fmt, Header)) then 
    begin
        raise EFileOperation.Create('Invalid matrix file format');
    end;

    if not((Header.Rows = RM) AND (Header.Cols = CM))then
    begin
        raise EInconsistentArray.Create('The dimensions of the matrix and file data do not match', itDimNotMatch);
    end;

    //Ahora vamos por el "Happy Path!"
    // Leemos data
    if Header.Orientation = aoCol
     then begin
            // ... por columnas
            for j := 0 to CM - 1 do
              for i := 0 to RM - 1 do
                FFile.ReadBuffer(AMatrix[i, j], SizeOf(TYPEDATA));
          end
     else begin
            // ... por filas
            for i := 0 to RM - 1 do
              for j := 0 to CM - 1 do
                FFile.ReadBuffer(AMatrix[i, j], SizeOf(TYPEDATA));
          end;
    end    
end;

procedure TArrayConverter.LoadMatrix(AMatrix: TAMatrix; FileName: string);
var Header: TMatrixHeader;
    Fmt: TFileFormat;
    i, j, RM, CM, Err, ConvE: integer;
    Can: boolean;
begin
    Can := CheckMatrix(AMatrix, RM, CM, Err);
    if not(Can AND (Err = OPERATION_DONE)) then 
    begin
        EInconsistentArray.Create('Check of matrix failed', itCheck)
    end;

    FFile = LoadFile(FileName);
    try
        ReadMatrix(AMatrix, FFile, RM, CM);
    finally
       FFile.Free;
    end;
end;

Podras notar varias cosas:

1- Eliminar el nesting hacer mucho mas legible el código, mas de 3 niveles de identacion suele ser una mala señal

2- Poner cerca la detección de los problemas, hace mas claro el codigo (y concuerda con "fallar rápido" ie: poner primero fallar, luego el "happy path")

3- Es mejor programar con el "happy path" o la ruta feliz: Una vez que estoy en la ruta feliz, se asume que todo debe funcionar ok, a menos que algo realmente inesperado suceda (como quedarse sin memoria) donde la única opción sana, es matar el programa.

4- El uso de "FInUse" no le veo sentido: Si lo que quieres es decir que el archivo esta en uso, no es tu codigo, sino el OS, quien realmente sabe si es verdad y quien es el responsable.

Si lo que quieres es evitar que se carguen 2 veces el archivo, esa no es la manera robusta.

5- Separar en funciones es algo que me gusta porque simplifica la lectura, pero en este caso con la reduccion de la identacion y mover la deteccion quedaria igual de chulo.


Aun mas que se puede hacer, pero no quise hacer un cambio muy radical, y como hace un rato que estoy oxidado con Delphi y estoy muy metido con otros lenguajes no quise hacer algo muy alienigena
__________________
El malabarista.
Responder Con Cita
  #5  
Antiguo 27-05-2015
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: jul 2004
Ubicación: Salta, Argentina
Posts: 5.582
Poder: 25
Delphius Va camino a la fama
Gracias Mamx por colaborar.

Estuve pensando en un método tipo PrepareStream() que bien o regrese un valor boolean o una excepción para determinar si algo ha fallado pero no he probado esta opción.

Tengo que pensar un poco tu propuesta. Se ve interesante el enfoque.

Te comento el porqué de la propiedad InUse. La clase está pensada a modo singleton (aunque no hay un control estricto en ello, y nada que impida crear varias instancias) debido a que es posible invocarla desde más de 2 módulos/clases independientes. De este modo, desde cualquier parte interesada en invocarla sepa si está disponible. No vi estrictamente necesario hacerlo por la vía Observer ya que no espero demasiada cosa.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #6  
Antiguo 27-05-2015
Avatar de Casimiro Notevi
Casimiro Notevi Casimiro Notevi is offline
Moderador
 
Registrado: sep 2004
Ubicación: En algún lugar.
Posts: 32.101
Poder: 10
Casimiro Notevi Tiene un aura espectacularCasimiro Notevi Tiene un aura espectacular
Creo recordar que en "La cara oculta de Delphi 4" había un capítulo sobre el tema.

Vaya situación personal la tuya, a ver si se arregla pronto, ánimos
Responder Con Cita
  #7  
Antiguo 27-05-2015
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: jul 2004
Ubicación: Salta, Argentina
Posts: 5.582
Poder: 25
Delphius Va camino a la fama
Cita:
Empezado por Casimiro Notevi Ver Mensaje
Creo recordar que en "La cara oculta de Delphi 4" había un capítulo sobre el tema.

Vaya situación personal la tuya, a ver si se arregla pronto, ánimos
Es cierto, hay un capítulo que trata la teoría de las excepciones, y habla de las reglas de oro de cómo y cuando usarlas. Pero el mismo al final aclara que incluso esas reglas están hechas para romperlas. Tengo que admitir que tengo oxidada esa parte. Y es justamente entre esas cosas, lo que extraño tratar de hacer memoria a como iba y por ello es que acudo a los foros, a ver quien me hecha luz.

El tiempo que estuve fuera de la programación si que ha oxidado mi cabeza

Lo de arreglar mis problemas, no creo que desaparezcan prontamente. Va a ser una batalla dura seguramente. Ni te imaginas cuanto deseo desapegarme de eso, pero lamentablemente tuve que estar bastante embarrado para evitar que otros, más indefensos que yo, salieran más lástimado y controlar a que la bestia le salte más de lo normal los tapones contra otros. Lo que esperaba de mi ausencia es poner en evidencia de que si no saltan más fuerte los tapones es porque justamente yo era quien venía frenando a esa bestia (algún "provecho" tenía que haberle sacado a lo poco que aprendí de arte marciales). Van a tener que aprender a defenderse solas, no es posible que deba dejar mi vida para evitar que las golpeen y tener que tener un ojo siempre despierto vigilando a ver que trama el loco. Bastante he salido a defendarlas cada vez que piden auxilio. Lamentablemente tengo que sacar mi yo egoista.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]

Última edición por Delphius fecha: 28-05-2015 a las 00:06:45.
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
Capturando excepciones en un archivo de texto noob Varios 5 20-02-2009 09:47:46
Duda sobre posibles excepciones en una desconexión de un socket noob Varios 0 13-02-2009 19:33:14
TMaskedit, con posibles excepciones en el formato grotero76 OOP 6 31-01-2008 13:49:23
Cómo utilizar consultas con DISTINCT de forma correcta dec MySQL 9 19-09-2006 17:50:47
lista de todas las posibles excepciones maruenda Varios 1 06-12-2004 22:31:02


La franja horaria es GMT +2. Ahora son las 12:07:36.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi