con - punteros como parametros de funciones en c
Pasar más parámetros en punteros de función C (9)
+1 a Antonio. Necesita cambiar su declaración de puntero de función para aceptar parámetros adicionales.
Además, no comience a pasar punteros vacíos o (especialmente) matrices de punteros vacíos. Eso es solo pedir problemas. Si comienza a pasar punteros vacíos, también tendrá que pasar algún tipo de mensaje para indicar cuál es el tipo de puntero (o tipos). Esta técnica rara vez es apropiada.
Si sus parámetros son siempre los mismos, simplemente agréguelos a los argumentos de su puntero de función (o posiblemente empaquételos en una estructura y utilícelo como argumento si hay muchos parámetros). Si sus parámetros cambian, considere usar múltiples punteros de función para los escenarios de llamadas múltiples en lugar de pasar los punteros vacíos.
Digamos que estoy creando un programa de ajedrez. Tengo una función
void foreachMove( void (*action)(chess_move*), chess_game* game);
que llamará a la acción del puntero de función en cada movimiento válido. Esto está muy bien, pero ¿y si necesito pasar más parámetros a la función de acción? Por ejemplo:
chess_move getNextMove(chess_game* game, int depth){
//for each valid move, determine how good the move is
foreachMove(moveHandler, game);
}
void moveHandler(chess_move* move){
//uh oh, now I need the variables "game" and "depth" from the above function
}
La redefinición del puntero a la función no es la solución óptima. La función foreachMove es versátil y muchos lugares diferentes en el código lo referencian. No tiene sentido que cada una de esas referencias tenga que actualizar su función para incluir parámetros que no necesitan.
¿Cómo puedo pasar parámetros adicionales a una función que estoy llamando a través de un puntero?
Ah, si solo C admite cierres ...
Antonio tiene razón; si necesita pasar parámetros adicionales, deberá redefinir su puntero de función para aceptar los argumentos adicionales. Si no sabe exactamente qué parámetros necesitará, entonces tiene al menos tres opciones:
- Haga que el último argumento en su prototipo sea nulo *. Esto le da flexibilidad para pasar cualquier otra cosa que necesite, pero definitivamente no es seguro para el tipo.
- Use parámetros variados (...). Dada mi falta de experiencia con los parámetros variados en C, no estoy seguro si puedes usar esto con un puntero a la función, pero esto da incluso más flexibilidad que la primera solución, aunque todavía con la falta de seguridad del tipo.
- Actualice a C ++ y use objetos de función .
Probablemente necesites redefinir el puntero de la función para tomar argumentos adicionales.
void foreachMove( void (*action)(chess_move*, int), chess_game* game )
Si cambian sus parámetros, cambiaría la declaración del puntero a la función para usar la técnica "..." para configurar una cantidad variable de argumentos. Puede ahorrarle legibilidad y también tener que realizar un cambio para cada parámetro que quiera pasar a la función. Definitivamente es mucho más seguro que pasar el vacío.
http://publications.gbdirect.co.uk/c_book/chapter9/stdarg.html
Solo un FYI, sobre el código de ejemplo en el enlace: algunos lugares tienen "n args" y otros es "n_args" con el guión bajo. Todos deberían tener el guión bajo. Pensé que la sintaxis parecía un poco divertida hasta que me di cuenta de que habían soltado el guión bajo en algunos lugares.
Si está dispuesto a usar C ++, puede usar un "objeto de función":
struct MoveHandler {
chess_game *game;
int depth;
MoveHandler(chess_game *g, int d): game(g), depth(d) {}
void operator () (chess_move*) {
// now you can use the game and the depth
}
};
y convierte tu foreachMove
en una plantilla:
template <typename T>
void foreachMove(T action, chess_game* game);
y puedes llamarlo así:
chess_move getNextMove(chess_game* game, int depth){
//for each valid move, determine how good the move is
foreachMove(MoveHandler(game, depth), game);
}
pero no interrumpirá tus otros usos de MoveHandler
.
Sugeriría usar una matriz de vacío *, con la última entrada siempre vacía. digamos que necesitas 3 parámetros, podrías hacer esto:
void MoveHandler (void** DataArray)
{
// data1 is always chess_move
chess_move data1 = DataArray[0]? (*(chess_move*)DataArray[0]) : NULL;
// data2 is always float
float data1 = DataArray[1]? (*(float*)DataArray[1]) : NULL;
// data3 is always char
char data1 = DataArray[2]? (*(char*)DataArray[2]) : NULL;
//etc
}
void foreachMove( void (*action)(void**), chess_game* game);
y entonces
chess_move getNextMove(chess_game* game, int depth){
//for each valid move, determine how good the move is
void* data[4];
data[0] = &chess_move;
float f1;
char c1;
data[1] = &f1;
data[2] = &c1;
data[3] = NULL;
foreachMove(moveHandler, game);
}
Si todos los parámetros son del mismo tipo, puede evitar la matriz void * y simplemente enviar una matriz con terminación NULL del tipo que necesite.
Otra opción sería modificar la estructura chess_move
lugar del prototipo de función. La estructura presumiblemente se define en un solo lugar ya. Agregue los miembros a la estructura y llene la estructura con los datos apropiados antes de cualquier llamada que la use.
Si estoy leyendo este derecho, lo que sugeriría es hacer que su función tome un puntero a una estructura como argumento. Entonces, su estructura puede tener "juego" y "profundidad" cuando los necesite, y simplemente déjelos en 0 o Nulo cuando no los necesite.
¿Qué está pasando en esa función? ¿Tienes un condicional que diga,
if (depth > -1) //some default
{
//do something
}
¿La función siempre REQUIERE "juego" y "profundidad"? Entonces, siempre deben ser argumentos, y eso puede entrar en tus prototipos.
¿Estás indicando que la función solo requiere a veces "juego" y "profundidad"? Bueno, tal vez haga dos funciones y use cada una cuando lo necesite.
Pero tener una estructura como argumento es probablemente lo más fácil.
Use un typedef para el puntero de la función. Ver mi respuesta para esta pregunta