Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > Varios
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 30-03-2008
Avatar de Guillermo80
Guillermo80 Guillermo80 is offline
Miembro
 
Registrado: may 2007
Posts: 87
Poder: 18
Guillermo80 Va por buen camino
Puntero a función

Hola foreros, tengo un problema al traducir una cabecera de una función de Visual C++, la cual es un puntero a una función. He mirado cómo debería hacerse pero me salen errores de compilación que no consigo resolver. La "original" es la siguiente:

Cita:
typedef void (*FLAC__StreamEncoderProgressCallback)(const FLAC__StreamEncoder *encoder);
Según he visto, debería quedar así en Delphi:

Cita:
var

pfuncion : Pointer;

pfuncion:procedure
FLAC__StreamEncoderProgressCallback
(encoder:PFLAC__StreamEncoder);
...pero no funciona, el primer problema parece ser que pfuncion está redeclarado, pero es así como venía en la "guía" que consulté, ¿qué idea se os ocurre?
Responder Con Cita
  #2  
Antiguo 30-03-2008
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
Hola Guillermo80,
¿Deseas traducir de Visual C++ a Delphi?

No se Visual C++ pero un puntero a función o procedimiento en Delphi es así:

Código Delphi [-]
type
  TMiFuncion = function MiFuncion(Algo: TAlgo): TAlgo;

var
  Funcion: TMiFuncion; //nuestra variable que apuntará a funciones del tipo TMiFuncion.

De modo que si tenemos una función cualquiera que cumpla con dicha descripción de tipos y argumentos podemos hacer cosas como esta:

Código Delphi [-]
funcion UnaFuncion(Algo: TAlgo): TAlgo;
begin
  // ... lo que venga aquí
  result := Algo; // es un ejemplo!
end;


begin
  // ....
  Funcion := UnaFunción;
  resultado := Funcion(AlgunaVariableOValor);
end;

No se si se entiende.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #3  
Antiguo 30-03-2008
Avatar de Guillermo80
Guillermo80 Guillermo80 is offline
Miembro
 
Registrado: may 2007
Posts: 87
Poder: 18
Guillermo80 Va por buen camino
de momento no sale

Hola Delphius, gracias por los ejemplos, se parece a lo que quiero hacer pero aún aplicando como me dices no me termina de salir. Para que entiendas un poco el contexto, se trata de un código en una Unit.pas que hace referencia a una librería compilada en Visual C++, de ahí el querer traducir la cabecera de este método. Este puntero a funcion es a su vez un parámetro de entrada de otra función que es la que hace uso de la dll. Finalmente mi código ha quedado como sigue:

Cita:
...
...
type

TFLAC__StreamEncoderProgressCallback = function FLAC__StreamEncoderProgressCallback

(encoder:PFLAC__StreamEncoder; bytes_written:FLAC__uint64; samples_written:FLAC__uint64;

total_frames_estimate:LongWord; client_data:Pointer):Pointer;


var

funcCallback : TFLAC__StreamEncoderProgressCallback; {no lo necesito, ya se lo paso a ..init_FILE con el parámetro progress_callback}

const
libFLAC = 'libFLAC.dll';

function FLAC__stream_encoder_init_FILE(encoder:PFLAC__StreamEncoder; pfile:PFile;
progress_callback:TFLAC__StreamEncoderProgressCallback; client_data:Pointer):FLAC__StreamEncoderInitStatus;cdecl; External libFLAC;
¿Qué es lo que estoy haciendo mal?
Responder Con Cita
  #4  
Antiguo 30-03-2008
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Poder: 30
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
Smile

¡Hola!

Me gustaría añadir que ese tipo de dato se conoce como procedimental (en inglés procedural).

Es recomendable tomar en cuenta el ejemplo dado por Marcelo, Guillermo. Aunque en este caso, el nombre original del tipo de dato indica que se trata de una función de retrollamada (callback). Este tipo de funciones generalmente no son llamadas explícitamente desde nuestro código, sino que se las proporcionamos a otra función para que ésta se encargue de llamarlas.

Las funciones de retrollamada son similares a los eventos, en el sentido de que proporcionamos una rutina de código para que un proceso ya escrito, al ser ejecutado, llame a nuestra rutina cada vez que suceda algo en particular. Es usual encontrarlas en muchas APIs que usan estas funciones de retrollamada para "avisarnos" o "actualizarnos" información, algunas veces en procesos iterativos.

Por la sintaxis nativa, me parece bien definir a tu función de retrollamada como procedimiento. Sólo habría que averiguar qué convención de llamada utiliza, indicándolo con la palabra reservada correspondiente: Register, Pascal, CDecl, StdCall, SafeCall (ver tema "Calling conventions" en la ayuda de Delphi).

Por último comento que no es necesario usar la palabra Pointer para indicar que nuestra variable o tipo de dato es procedimental. En Pascal es implícito que un valor procedimental es la dirección de memoria (puntero) de la primera instrucción de código máquina de una rutina —es a esa dirección a donde "salta" la CPU para ejecutarla—.

Mi aportación.

Al González.

P.D. También te sugiero, Guillermo, llamarle a estas APIs o DLLs bibliotecas en lugar de "librerías". Es más correcto.
Responder Con Cita
  #5  
Antiguo 30-03-2008
Avatar de Guillermo80
Guillermo80 Guillermo80 is offline
Miembro
 
Registrado: may 2007
Posts: 87
Poder: 18
Guillermo80 Va por buen camino
Hola Al, muchas gracias por esta clase magistral , me aclara bastante las ideas de lo que estoy manejando, ya que había cosas que no tenía clara.

He estado mirando un poco más a fondo las directivas y en este caso creo que está claro que se usa register, que es la que permite definir tipos con parámetros. Así que ahora lo tengo definido así, pero aún así no me funciona:

Cita:
type

TFLAC__StreamEncoderProgressCallback = procedure FLAC__StreamEncoderProgressCallback
(encoder:PFLAC__StreamEncoder; bytes_written:FLAC__uint64; samples_written:FLAC__uint64; total_frames_estimate:LongWord; client_data:Pointer);register;

var

funcCallback : TFLAC__StreamEncoderProgressCallback;
¿Por qué me falla la compilación? El error que me sale es directiva desconocida para FLAC__StreamEncoderProgressCallback.
Responder Con Cita
  #6  
Antiguo 30-03-2008
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 Guillermo80 Ver Mensaje
Hola Al, muchas gracias por esta clase magistral , me aclara bastante las ideas de lo que estoy manejando, ya que había cosas que no tenía clara.

He estado mirando un poco más a fondo las directivas y en este caso creo que está claro que se usa register, que es la que permite definir tipos con parámetros. Así que ahora lo tengo definido así, pero aún así no me funciona:

¿Por qué me falla la compilación? El error que me sale es directiva desconocida para FLAC__StreamEncoderProgressCallback.
Admito que las directivas que ha mencionado el buen amigo Al no las he empleado, por lo que tengo que invesigarlas un poco, pero ¿Podrías indicarnos concretamente el error? En una de esas conocer el error exacto ayuda.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #7  
Antiguo 31-03-2008
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Poder: 30
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
Smile

Cita:
Empezado por Guillermo80 Ver Mensaje
...He estado mirando un poco más a fondo las directivas y en este caso creo que está claro que se usa register, que es la que permite definir tipos con parámetros...El error que me sale es directiva desconocida para FLAC__StreamEncoderProgressCallback
mmm ...creo que no lo has comprendido del todo bien. Dudo mucho que Register sea la directiva adecuada para este caso. Normalmente las DLLs escritas en C++ usan la convención de llamada StdCall o SafeCall. Si la documentación de la biblioteca no lo indica, lo más probable es que debas usar StdCall.

El error que te aparece seguramente es Unknown directive: 'FLAC__StreamEncoderProgressCallback'. Habla bien de un forista mostrar el mensaje exacto de error que le aparece en pantalla, más que una interpretación personal (y a veces equivocada) del mismo.

Ese error aparece porque le estás dando el nombre del tipo de dato después de la palabra Procedure; basta con que el nombre aparezca una sola vez delante (a la izquierda) del signo de igual ("="), como ya lo está ahí. Después de "Procedure" deben ir los parámetros entre paréntesis, es decir, la cabecera.

Por otro lado, desconozco por qué declaras la variable funcCallback (tendría que ver tu código para decirte si realmente la requieres). Lo que sí debes de tener claro, Guillermo, es que debes* crear un procedimiento con la misma cabecera que define TFLAC__StreamEncoderProgressCallback, para poder darle ese procedimiento a la función que lo espera como parámetro.

Ese procedimiento que vas a crear, es tu rutina de retrollamada.

*¿Qué dice la documentación de la biblioteca? ¿Forzosamente debes proporcionar ese procedimiento? Algunas veces las funciones de retrollamada son opcionales.

Espero haber orientado.

Al González.

Última edición por Al González fecha: 31-03-2008 a las 00:57:43.
Responder Con Cita
  #8  
Antiguo 01-04-2008
Avatar de Guillermo80
Guillermo80 Guillermo80 is offline
Miembro
 
Registrado: may 2007
Posts: 87
Poder: 18
Guillermo80 Va por buen camino
Hola Al, muchas gracias por tu aportación, sin duda está siendo muy valiosa para mí.
He aplicado las cosas como me dijiste y efectivamente, ya compila y ejecuta, pero hay alguna cosa que no tengo clara.

La función de retrollamada tienes razón, es opcional, y le puedo pasar un nil a la función principal en el parámetro de la función de retrollamada, pero finalmente la he incluido.

Mi código ha quedado como sigue de forma resumida:

Cita:
type

FLAC__StreamEncoderProgressCallback = procedure
(encoder:PFLAC__StreamEncoder; bytes_written:FLAC__uint64; samples_written:FLAC__uint64;total_frames_estimate:LongWord; client_data:Pointer);StdCall;
Yo me declaro una variable para meterla como parámetro a la función principal:
Cita:
var

funcCallback : FLAC__StreamEncoderProgressCallback;
Usando en mi función ppal:

Cita:
begin
...
init_status := FLAC__stream_encoder_init_file(pencoder, pfileDestino, funcCallback, nil);
Aunque entiendo que está incompleto porque funcCallback debe inicializarse de alguna forma, pero no entiendo como hacer exactamente lo que me comentas de: "debes* crear un procedimiento con la misma cabecera que define TFLAC__StreamEncoderProgressCallback, para poder darle ese procedimiento a la función que lo espera como parámetro".

¿Tendría que crear un procedimiento con otro nombre?¿Cómo lo usaría para poder "alimentar" mi función principal correctamente?
En VisualC lo hace así:

Cita:
static void progress_callback(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data);
...
int main(int argc, char *argv[])
...
init_status = FLAC__stream_encoder_init_file(encoder, argv[2], progress_callback, NULL);
El parámetro NULL (parámetro formal void client_data) se supone que es como dice textualmente: \param client_data. This value will be supplied to callbacks in their \a client_data argument. O sea, que serviría para las retrollamadas??

Un saludo.
Responder Con Cita
  #9  
Antiguo 02-04-2008
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Poder: 30
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
Smile

Cita:
Empezado por Guillermo80 Ver Mensaje
...Aunque entiendo que está incompleto porque funcCallback debe inicializarse de alguna forma, pero no entiendo como hacer exactamente lo que me comentas de: "debes* crear un procedimiento con la misma cabecera que define TFLAC__StreamEncoderProgressCallback, para poder darle ese procedimiento a la función que lo espera como parámetro".

¿Tendría que crear un procedimiento con otro nombre?¿Cómo lo usaría para poder "alimentar" mi función principal correctamente?...
Primero asegúrate de si la función de retrollamada es necesaria u opcional. En caso de ser necesaria o de querer darle una a la función Init_File, habrás de crear, efectivamente, un procedimiento con el nombre que quieras (mientras no tenga conflicto con otros identificadores, claro está), siendo ese procedimiento de cabecera equivalente al tipo de dato FLAC__StreamEncoderProgressCallback que ya declaraste.

Una vez escrito ese procedimiento, para hacer referencia a la dirección de memoria del mismo, puedes emplear el operador "at" (la arroba):

Código Delphi [-]
init_status := FLAC__stream_encoder_init_file(pencoder, pfileDestino, @MiProcedimiento, Nil);

Con esto le dices a FLAC__stream_encoder_init_file que la función de retrollamada que debe ejecutar es MiProcedimiento. Al parecer, el último parámetro (Nil) es algún valor que tu quieras darle a la función de retrollamada.

Es como decirle a la función Init_File: "Oye, te encargo que ejecutes a MiProcedimiento, y al hacerlo incluye en sus parámetros este valor".

También es importante que entiendas bien todos los conceptos que estás manejando. Para empezar sería bueno, si no te molesta, comentarnos qué propósito tiene esa API que estás usando y cuál es el objetivo.

Saludos.

Al.
Responder Con Cita
  #10  
Antiguo 02-04-2008
Avatar de Guillermo80
Guillermo80 Guillermo80 is offline
Miembro
 
Registrado: may 2007
Posts: 87
Poder: 18
Guillermo80 Va por buen camino
me he perdido

Hola Al, lo siento pero creo que no te he entendido lo último que me decías:

Cita:
...habrás de crear, efectivamente, un procedimiento con el nombre que quieras (mientras no tenga conflicto con otros identificadores, claro está), siendo ese procedimiento de cabecera equivalente al tipo de dato FLAC__StreamEncoderProgressCallback que ya declaraste
Si ahora resulta que a Init_File le voy a pasar @MiFuncion:

1. ¿Cómo se relaciona con el tipo que me he creado FLAC__StreamEncoderProgressCallback? Quiero decir, que ahora tendría un tipo FLAC__StreamEncoderProgressCallback, una función @Mifunción, y sólo un parámetro para pasarle a Init_File.
2. Si utilizo como me dices como parámetro para init_File @MiFuncion, eso falla en compilación, porque le estaría pasando una direccion de memoria en vez de una variable declarada del tipo precisado. ¿Qué es lo que falla?

Aparte de todo lo anterior te explico un poco el contexto:

- El programa general consiste en comprimir audio sin pérdidas siguiendo las especificaciones del formato de audio FLAC.
- Para ello uso las funciones de libFlac.dll
- El programa en realidad "no me lo invento", en un principio se trata de hacer algo simple, para ello tomo un ejemplo de VisualC++ que comprime una pista de audio.
- La función de retrollamada es opcional, y si la cosa se pone fea, pues le paso un nil y se acabó.
- El punto del programa en el que se hace la retrollamada es el siguiente:

Cita:
init_status = FLAC__stream_encoder_init_file(encoder, argv[2], progress_callback, NULL);
Pongo textualmente la descripción del sistema que nos ocupa:

Cita:
* For encoding directly to a file, use FLAC__stream_encoder_init_FILE()
* or FLAC__stream_encoder_init_file(). Then you must only supply a
* filename or open \c FILE*; the encoder will handle all the callbacks
* internally. You may also supply a progress callback for periodic
* notification of the encoding progress.Initialize the encoder instance to * * encode native FLAC files.
...
...
* \param encoder An uninitialized encoder instance.
* \param file An open file. The file should have been opened
* with mode \c "w+b" and rewound. The file
* becomes owned by the encoder and should not be
* manipulated by the client while encoding.
* Unless \a file is \c stdout, it will be closed
* when FLAC__stream_encoder_finish() is called.
* Note however that a proper SEEKTABLE cannot be
* created when encoding to \c stdout since it is
* not seekable.
* \param progress_callback See FLAC__StreamEncoderProgressCallback. This
* pointer may be \c NULL if the callback is not
* desired.
* \param client_data This value will be supplied to callbacks in their
* \a client_data argument.
De aquí hay una cosa que no entiendo exactamente, cuando dice:

Cita:
...The file should have been opened with mode \c "w+b" and rewound.
¿En Delphi a qué equivaldría? Yo el fichero lo abro en modo lectura, aunque ahora dudo si debería estar en lectura/escritura por si codifica en el mismo fichero y no en otro...

Espero haber provisto de información suficiente mi trabajo.

Un saludo.

Guillermo.
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
¿puntero a puntero? Guillermo80 Varios 3 09-03-2008 22:46:32
puntero gaston260 API de Windows 11 01-02-2008 22:39:20
uso de función de dll cuyo parametro es un puntero (dirección) a una estructura abracadabra Varios 1 12-10-2007 16:43:59
pasar un puntero a una funcion baby Varios 2 21-05-2007 13:10:22
puntero a imagen Abaddon Gráficos 9 16-06-2005 00:17:29


La franja horaria es GMT +2. Ahora son las 06:42:49.


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