Club Delphi  
    Paypal   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 Buscar Temas de Hoy Marcar Foros Como Leídos

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 29-05-2015
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.619
Poder: 32
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Hola Marcelo.

Cuatro cosas:

1. Gracias por regresar al foro. Da gusto ver cómo durante el último año han estado integrándose y reintegrándose muchos colegas en el Club. Muy de la mano, es evidente que el rescate de Delphi se va consolidando (y por añadidura el repunte de otros lenguajes Object Pascal).

2. En México tienes abiertas la puertas de mi humilde hogar, si te agrada la idea y te es posible viajar, no tienes más que avisar. Y si es necesario buscamos la forma de facilitarte el traslado. Entre nosotros hay mucho código y conocimiento que podríamos compartir presencialmente. En fin, es una invitación a que te desconectes aunque sea unos meses de aquel ambiente.

3. Sostengo los comentarios técnicos que escribí hace varios años en el hilo que refieres, incluso ahora estoy más convencido de ellos.

4. Si este fin de semana no me distraen mucho acá, revisaré con detenimiento tu caso y responderé aquí con lo que pueda ayudar.

Saludos.

Al.
__________________
Tras casi seis años de trabajar para una empresa alemana como desarrollador Delphi, se vieron forzados a dejarme ir (temas presupuestales). Así que ahora estoy abierto a escuchar nuevas ofertas.
Responder Con Cita
  #2  
Antiguo 29-05-2015
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: jul 2004
Ubicación: Salta, Argentina
Posts: 5.582
Poder: 28
Delphius Va camino a la fama
Cita:
Empezado por mamcx Ver Mensaje
Asi como lo expresas, entonces me parece que es bueno seguir tal como es FileStream. Crear TArrayConverter si el archivo no se puede leer carece de sentido, mientras que operar sobre TArrayConverter puede no siempre tener problemas, asi que en cada metodo se evalua que hacer.

Mejor dicho:

Código Delphi [-] TArrayConverter = class private FFile: TFileStream; .. .. public //Aqui se hace lo de LoadFile. si esto falla, el objeto //no tiene razon de existir constructor Create(FileName: string); destructor Destroy; override; procedure LoadMatrix(AMatrix: TAMatrix); procedure LoadVector(AVector: TAVector); procedure SaveMatrix(AMatrix: TAMatrix; OnDir: TArrayOrientation); procedure SaveVector(AVector: TAVector); end;


Asi que quien llama a TArrayConverter con el archivo X solo tiene 2 opciones: Se puede o no operar sobre el archivo, si no se puede, ya haces como has dicho.

Si el objeto TArrayConverter existe, los errores son solo probables y el objeto reacciona de acuerdo.

Asi se captura de forma muy explicita lo que estas diciendo, sin complicar la logica interna del objeto. Ademas, mientras exista TArrayConverter se asume que el archivo esta en uso, lo que anula la variable de InUse que existe ahora porque TArrayConverter esta en un estado potencialmente dual: Tiene o no acceso?


Si entiendo bien lo de punto 1 & 3, entonces no veo porque pasas la matriz, en vez de retornarla tal como indique el archivo, o sea:

Código Delphi [-]function LoadMatrix():TAMatrix;


Ademas, si estas invocando de multiples sitios ese metodo, tendras problemas de concurrencia y tendrias que aplicar bloqueos u otra opcion para asegurar el acceso concurrente.

Es mas simple cuando los objetos son inmutables, y la informacion no se comparte (crea un cuello de botella). Mientras no se muchisimos datos, es muy rapido recrear una matriz y que cada parte del programa tenga su propia copia sabiendo con certeza que nadie la va a alterar.
Muchas gracias mamx (no recuerdo bien si tu nombre era Mario, y a mi me gusta en lo posible dirigirme más en forma personal) por tu valioso aporte y ayudarme.

De lo que estoy entendiendo de tu propuesta, es hacer de TArrayConverter una especie de Adapter del TFileStream y que en caso de poder crear una instancia de TArrayConverter proceda a utilizarla. De ser así en realidad no soluciona el mayor problema: que no se pueda crear el TArrayConverter es lo mismo que no se pueda crear el TFileStream.
Tal diseño directamente pone en evidencia que no tiene sentido la clase y directamente se haga uso de TFileStream ¿no crees?
Entonces las clases que eran clientes de TArrayConverter, que directamente, hagan uso de TFileStream.

Si yo estoy entendiendo mal el concepto por favor hazmelo saber.

La intención de contar con TArrayConverter es que ésta pueda centrar el trabajo común de leer y guardar de archivos. Otros módulos/clases tienen ya sus propios juegos de matrices y vectores. Entre ellas se comparten algunas estructuras comunes, y otras son propias. Cada módulo/clase aplica sus instrucciones sobre estas estructuras y varias son de gran importancia e interés poder materializarlas en un archivo para usos posteriores.
Debido a ello es que vi natural el que exista una instancia de TArrayConverter a modo singleton que reciba las estructuras de cualquiera de estos módulos/clases y haga lo que mejor sabe hacer.

No consideré prudente que un LoadMatrix() regrese el tipo de dato TMatriz como sugieres debido a que esto condiciona a que el conflicto de intereses entre quien es el dueño de la matriz y no incluirle lógica que ya es más propia de otras clases.

Por cuestiones de operatoria y diseño es raro que se necesite un intento de leer y/o guardar archivos de forma concurrente o simultáneo. Generalmente se da cierto orden secuencial. Pero por seguridad, y para esos casos en que tales archivos sean grandes (según pruebas algunos archivos si que serán grandes... entre los 4MB a 10MB en promedio pero puede darse situaciones de mayor tamaño), es que vi sano el añadir la propiedad InUse o alguna tipo flag que indique que el objeto está ocupado trabajando en ese momento. De ese modo pretendía dos cosas:
1. Que TArrayConverter cree el TFileStream y lo libere cuando se necesite trabajar con algún archivo (recién me percato que posiblemente sea un error disponer de un atributo privado)
2. Que al disponer de esta propiedad InUse permita cierto "relajo" a la aplicación y permita darle respiros ante la cantidad de operaciones que se realizan entre cada lectura/guardado de archivos.

Cita:
Empezado por Al González Ver Mensaje
Hola Marcelo.

Cuatro cosas:

1. Gracias por regresar al foro. Da gusto ver cómo durante el último año han estado integrándose y reintegrándose muchos colegas en el Club. Muy de la mano, es evidente que el rescate de Delphi se va consolidando (y por añadidura el repunte de otros lenguajes Object Pascal).

2. En México tienes abiertas la puertas de mi humilde hogar, si te agrada la idea y te es posible viajar, no tienes más que avisar. Y si es necesario buscamos la forma de facilitarte el traslado. Entre nosotros hay mucho código y conocimiento que podríamos compartir presencialmente. En fin, es una invitación a que te desconectes aunque sea unos meses de aquel ambiente.

3. Sostengo los comentarios técnicos que escribí hace varios años en el hilo que refieres, incluso ahora estoy más convencido de ellos.

4. Si este fin de semana no me distraen mucho acá, revisaré con detenimiento tu caso y responderé aquí con lo que pueda ayudar.

Saludos.

Al.
Hola Al, gracias por venir en mi ayuda. Pido disculpas por haberte molestado en forma privada pero es que ya mi cabeza no trabaja tan bien después de haberme mandado cerca de 5000 líneas de código en otros módulos previos a éste. Y sumándose a que por cosas de la vida ya he perdido mucha práctica al estar bastante alejado de la programación.

Si bien tengo más presencia en los últimos tiempos en DA, no quiere decir que no estime a algunos compañeros. Como te dije: la comunidad Delphi es una.

Te agradezco la invitación, y admito que tengo ganas de buscar otros aires. Ganas no me faltan de ir a México y visitar a toda la pandilla, pero por ahora no podrá ser. Ya en los próximos días debo volver a casa, acá tengo a conocidos que me están dando apoyo pero también me ponen en ultimatum para que concrete para éste Lunes 1 (fecha en que posiblemente viaje).
Ni modo, no es fácil explicar a quien no está en el tema que un sistema no puede estar a medias. No es que se puede dejar como esté y que ande.

Deberé regresar y ver el modo de terminarlo allí.
No pensé que esto me tomara tanto tiempo. Necesito mínimo otra semana más si no hay más imprevisto y todo sale a la perfección.


Te agradezco cualquier recomendación.

Les comento a ambos que en lo que estoy pensando es aplicar una lógica que siga este diseño:

Código Delphi [-]
procedure TArrayConverter.LoadMatrix(...)
var File: TFileStream;
begin
  try
    File := TFileStream.Create(...);
    try
      // Hacer todo el trabajo
    finally
      File.Free;
    end;
  except
    // capturar excepciones dadas por TFileStrem y/o propagar las propias de TArrayConverter
  end;
end;

Lo que estoy divagando es ver como adaptar el algoritmo que puse en mi primer post a este esquema lo más limpio posible. ¿Que piensan?

De este modo alguna clase que llame a ésta haga algo como:

Código Delphi [-]
procedure TOtraClase.HacerAlgo;
begin
   HagoAlgoConEstaMatriz(LaMatrix);
   // y otras cosas más...
   try
      Conversor.SaveMatrix(LaMatrix, NombreDelArchivo, Orientacion); // Conversor es singleton!
   except
      E: EFileOperation do
      ....
end;

Y la versión análoga para una lectura.

¿Como lo ven?

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #3  
Antiguo 30-05-2015
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: jul 2004
Ubicación: Salta, Argentina
Posts: 5.582
Poder: 28
Delphius Va camino a la fama
Actualizo el hilo para comentarles que he hecho algunos cambios y redefinido un poco el diseño de la clase. He intentado hacer que el código siguiera la estructura de dos try anidados. El try-finally interior asume las condiciones ideales, y el exterior es un try-except para capturar las posibles excepciones que podrían darse.

la 2da versión, y quizá un poco más pulida, es:

Código Delphi [-]
procedure TArrayConverter.LoadMatrix(AMatrix: TAMatrix; FileName: string);
var idx, i, j, RowsM, ColsM, ErrM: integer;
    AFile: TFileStream;
    Can: Boolean;
    Fmt: TFileFormat;
    Header: TMatrixHeader;
begin
  Can := CheckMatrix(AMatrix, RowsM, ColsM, ErrM);
  try
    AFile := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
    Try
      FIsBusy := true;
      // Leemos formato y Header
      Fmt.SizeFile := AFile.Seek(0, soEnd);
      AFile.ReadBuffer(Fmt.IDIni, SizeOf(Fmt.IDIni)); // ID.Ini
      AFile.ReadBuffer(Header, SizeOf(Header)); // Header
      AFile.Seek(INI_ID_END, soFromEnd);
      AFile.ReadBuffer(Fmt.IDEnd, SizeOf(Fmt.IDEnd)); // ID.End

      // Posibles excepciones de formato y Header
      if NOT IsValidFormat(Fmt, Header)
         then raise EInvalidFileArrayFormat.Create(Format(sInvalidFormat,['matrix']));
      if (Header.Cols <> ColsM) OR (Header.Rows <> RowsM)
         then raise EInconsistArray.Create(sInconsistArray);

      // Operamos
      if Header.Orientation = aoCol
         then for Idx := 1 to (Header.Rows * Header.Cols) do
              begin
                i := (Idx - 1) mod Header.Rows;
                j := (Idx - 1) div Header.Rows;
                AFile.ReadBuffer(AMatrix[i, j], SizeOf(TYPEDATA));
              end
         else for Idx := 1 to (Header.Rows * Header.Cols) do
              begin
                i := (Idx - 1) div Header.Cols;
                j := (Idx - 1) mod Header.Cols;
                AFile.ReadBuffer(AMatrix[i, j], SizeOf(TYPEDATA));
              end;
    finally
      Afile.Free;
      fIsBusy := false;
    end; // end-try-finally
  except
    on E: EFOpenError do
    begin
      raise EFileAccessDenied.Create(Format(sAccessDenied, [FileName, E.Message]));
    end;
    on E: EReadError do
    begin
      raise EConvertFailed.Create(Format(sConvertFailed, ['read', FileName, E.Message]));
    end;
    // ¿Este except captura las excepciones lanzadas en el try interno y debiera relanzarlas?
  end; // end-try-except
end;

La duda que me surge, es si el try-except externo captura las excepciones arrojadas por el interno, y de ser así debiera de relanzarlas.
El compilador no protesta, pero no he probado el código por falta de tiempo y ya me gana el cansancio.
En la versión anterior, al evaluar el estado de la matriz con CheckMatrix() primero verificaba el resultado de dicha operación y a posterior comprobaba las variables de control que éste regresa (RM, CM) con la información leída del Header para determinar si efectivamente la matriz y el archivo tengan la misma dimensión y por tanto ni sobre ni falten datos.

Ahora directamente no hago esta distinción y asumo que ambos escenarios son del mismo tipo de error. Delego en la clase cliente la tarea de verificar tanto que la matriz efectivamente esté disponible (y por tanto al invocarse a CheckMatriz se lea su tamaño correcto) como la de que controle lo mejor posible que sus archivos estén en orden. Por diseño de CheckMatriz cuando la evaluación falla regresa un "código" de error distinto a OPERATION_DONE las variables de control se establecen en -1.

Rediseñé las excepciones, y propuse una nueva forma de nombrarlas.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #4  
Antiguo 31-05-2015
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: jul 2004
Ubicación: Salta, Argentina
Posts: 5.582
Poder: 28
Delphius Va camino a la fama
Sigo avanzando.
Esta nueva propuesta está dentro de todo funcionando, según las pruebas que he estado llevando a cabo. El diseño si captura las excepciones generadas por TFileStream y genera las propias.

Un típico modo de uso, sería algo:

Código Delphi [-]
try
  LogOperation('SaveMatrix');
  Converter.SaveMatrix(myM1, TArrayOrientation(rgDir.ItemIndex + 1), myFile);
  LogFile(myFile);
 except
  on E: EConverterException do
    LogException(E);
end;

LogOperation() y LogException() son dos métodos que he implementado en un sistema básico para prueba de caja blanca (y algo de "caja gris") que van registrando en un Memo a modo Log cada test que se realiza.
En el caso de LogException() se busca capturar las excepciones y mostrar el nombre de la clase y el mensaje.

He advertido que el algoritmo tiene dos bugs que ya he procedido a eliminar:
1. Al momento de hacer una lectura del Identificador inicial debiera de invocar, por seguridad es apropiado hacer previamente un Seek(0, soFromBeginning)
2. Al momento de proceder a leer los datos de igual forma se debe posicionarse en el primer elemento y para ello es necesario un Seek(INI_DATA_M, soFromBeginning) siendo INI_DATA_M una constante apropiada para el caso de un archivo diseñado para matrices.

Estos mismos problemas detectados fueron eliminados en el método LoadVector.

Esta versión optimiza el indexado del posicionamiento al leer la data. Inicialmente procedía con dos ciclos anidados. Gracias a la tan bella matemática se puede prescindir de un ciclo y directamente hacer la correspondencia entre el índice Idx del dato y su posición [i, j] en la matriz tanto en un lectura columna por columna como fila a fila.

Estoy abierto a las sugerencias.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #5  
Antiguo 01-06-2015
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.619
Poder: 32
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Hola Marcelo.

Este fin de semana tuve algo de tiempo libre para leer con detenimiento todo lo planteado por ti y Mario. ¡Cuántas cosas me gustaría decir! Como procurar la redacción en un sólo idioma y de manera más comprensible. O evitar en lo posible escribir métodos de más de 20 líneas de código. Pero intentaré enfocarme en lo principal de tu planteamiento técnico.

El uso canónico de un bloque Try-Finally es:
Código Delphi [-]
  // 1. Código para hacer algo que deseamos luego deshacer, ocurra lo que ocurra.  Crear, abrir, poner...
  Try
    // 2. Código que opera sobre lo hecho por el punto 1 y que tiene potencial de elevar una excepción.
  Finally
    // 3. Código que deshace lo hecho antes del Try.  Destruir, cerrar, quitar...
  End;
El uso canónico de un bloque Try-Except es:
Código Delphi [-]
  // 4. Código para hacer algo que, en caso de ocurrir un problema, quisiéramos deshacer.  Crear, abrir, poner...
  Try
    // 5. Código que podría generar alguna excepción sobre la cual deseamos tener control.
  Except
    { 6. Código que deshace lo hecho antes del Try (destruir, cerrar, quitar...) y/o trata de forma particular la
      excepción generada (por clase, código de error, etc.). }
  End;
El punto 6 consiste, generalmente, en tomar alguna diligencia de control, liberar algún recurso que previamente se cargó en memoria, registrar o reportar una bitácora de incidencias, o anexar/convertir la excepción en otra excepción. Pero casi siempre este tratamiento termina elevando una excepción (propagando la misma o instanciando otra) al llamador. En algunas ocasiones el punto 4 podría ser opcional.

Por otra parte, además de la muy mala práctica de sofocar excepciones (atraparlas solo "para que no estorben"), existe cierta práctica no muy buena (y afortunadamente poco difundida) de atrapar una excepción para convertirla en un código de resultado. Nunca hagas eso a menos que no tengas alternativa. Es más válido que un método de aplicación envuelva a un método de API, atrapando una excepción de esa API para transformarla en una excepción más propia de la aplicación (algo que ya veo haces). Aunque también es válido dejar que la excepción original aparezca en la pantalla del usuario. Lo más malo es que aparezca cualquier mensaje de error, no que el mensaje de error sea algo "geek". Claro, podría ser deseable que las excepciones digan algo entendible, pero no olvidemos que son eso: excepciones que no debieran presentarse.

Dada la introducción anterior, me permito poner en seudocódigo cómo podría ser tu método LoadMatrix:
Código Delphi [-]
LoadMatrix
Begin
  CheckMatrix (M);  // CheckMatrix no sólo verifica, también eleva excepción en caso de encontrar invalidez.
  —Punto 1. Crear FileStream

  Try
    —Punto 2. Uso del FileStream:
    Try
      —Punto 5. Leer archivo, verificar datos y elevar excepciones de tu clase en caso de inconsistencias.
    Except;
      —Punto 6. Atrapar excepciones conocidas o esperadas de FileStream y convertirlas en una excepción de tu clase.
    End;
    // Termina punto 2 (uso del FileStream).
  Finally
    —3. Destruir FileStream
  End;
End;
Espero que esta participación sea de alguna ayuda.
__________________
Tras casi seis años de trabajar para una empresa alemana como desarrollador Delphi, se vieron forzados a dejarme ir (temas presupuestales). Así que ahora estoy abierto a escuchar nuevas ofertas.
Responder Con Cita
  #6  
Antiguo 01-06-2015
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.619
Poder: 32
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Ah, pero resulta que el punto 1, es para ti punto 5 también. Entonces apliquemos la misma lógica:
Código Delphi [-]
LoadMatrix
Begin
  CheckMatrix (M);  // CheckMatrix no sólo verifica, también eleva excepción en caso de encontrar invalidez.

  Try
    —Punto 1 y 5. Crear FileStream

    Try
      —Punto 2. Uso del FileStream:
      Try
        —Punto 5. Leer archivo, verificar datos y elevar excepciones de tu clase en caso de inconsistencias.
      Except;
        —Punto 6. Atrapar excepciones conocidas o esperadas de FileStream y convertirlas en una excepción de tu clase.
      End;
      // Termina punto 2 (uso del FileStream).
    Finally
      —3. Destruir FileStream
    End;
  Except
    —Punto 6. Atrapar excepciones esperadas de la creación del FileStream y convertirlas en una excepción de tu clase.
  End;
End;
¿Mucha anidación?...¿Recuerdas lo de tener un límite de líneas? Divide un método largo en varios cortos y vencerás toda complejidad.
__________________
Tras casi seis años de trabajar para una empresa alemana como desarrollador Delphi, se vieron forzados a dejarme ir (temas presupuestales). Así que ahora estoy abierto a escuchar nuevas ofertas.

Última edición por Al González fecha: 01-06-2015 a las 19:09:56.
Responder Con Cita
  #7  
Antiguo 01-06-2015
Avatar de nlsgarcia
[nlsgarcia] nlsgarcia is offline
Miembro Premium
 
Registrado: feb 2007
Ubicación: Caracas, Venezuela
Posts: 2.206
Poder: 23
nlsgarcia Tiene un aura espectacularnlsgarcia Tiene un aura espectacular
Alberto,

Cita:
Empezado por Al González
...Msg #22 y Msg #23...
Excelente manejo de las excepciones

Nelson.
Responder Con Cita
Respuesta


Herramientas Buscar en Tema
Buscar en Tema:

Búsqueda Avanzada
Desplegado

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 17:32:26.


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