Transmiterea mai multor parametri în indicatorii funcției C

Să spunem că creez un program de șah. Am o funcție

void foreachMove( void (*action)(chess_move*), chess_game* game); 

care va apela acțiunea pointerului funcției pentru fiecare mișcare valabilă. Acest lucru este bine și bine, dar dacă am nevoie să trec mai mulți parametri la funcția de acțiune? De exemplu:

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
}

Redactarea pointerului funcției nu este soluția optimă. Funcția foreachMove este versatilă și multe locuri diferite în codul de referință. Nu are sens ca fiecare dintre aceste referințe să fie nevoită să își actualizeze funcția pentru a include parametrii de care nu au nevoie.

Cum pot trece parametri suplimentari la o funcție pe care o sun printr-un pointer?

0
fr hi bn

8 răspunsuri

Probabil că trebuie să redefiniți indicatorul funcției pentru a lua argumente suplimentare.

void foreachMove( void (*action)(chess_move*, int), chess_game* game )
0
adăugat

Use a typedef for the function pointer. See my answer for this question

0
adăugat

Aș sugera să folosiți o serie de void *, cu ultima intrare întotdeauna nulă. spunem că ai nevoie de 3 parametri pe care ai putea să le faci:

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);

și apoi

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);
}

Dacă toți parametrii sunt de același tip, atunci puteți evita matricea * void și trimiteți doar o matrice terminată cu NULL de orice tip aveți nevoie.

0
adăugat

+1 lui Antonio. Trebuie să modificați declarația indicatorului funcției pentru a accepta parametri suplimentari.

De asemenea, vă rugăm să nu începeți să treceți prin pointeri void sau (mai ales) arrays de pointers void. Asta doar cere probleme. Dacă începeți să treceți pointeri void, va trebui să treci și un fel de mesaj pentru a indica tipul tipului de pointer (sau tipuri). Această tehnică este rareori potrivită.

Dacă parametrii dvs. sunt întotdeauna aceiași, trebuie doar să le adăugați la argumentele pointerului funcției (sau eventual să le împachetați într-un struct și să folosiți acest argument ca argument dacă există o mulțime de parametri). Dacă parametrii se modifică, luați în considerare utilizarea mai multor indicatori de funcții pentru scenariile de apeluri multiple, în loc de a trece indicii void.

0
adăugat

Dacă sunteți dispus să utilizați un C ++, puteți utiliza un "obiect funcțional":

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
    }
};

și transformați foreachMove într-un șablon:

template 
void foreachMove(T action, chess_game* game);

și o puteți numi astfel:

chess_move getNextMove(chess_game* game, int depth){
    //for each valid move, determine how good the move is
    foreachMove(MoveHandler(game, depth), game);
}

dar nu va perturba alte utilizări ale MoveHandler .

0
adăugat

Dacă parametrii dvs. se modifică, aș schimba declarația indicatorului funcției pentru a folosi tehnica "..." pentru a configura un număr variabil de argumente. Ar putea să vă salveze citirea și, de asemenea, să faceți o schimbare pentru fiecare parametru pe care doriți să-l transmiteți funcției. Este cu siguranță mult mai sigur decât trecerea void în jurul.

http://publications.gbdirect.co.uk/c_book/chapter9/stdarg. html

Doar un FYI, despre exemplul codului din link: unele locuri pe care le au? și altele este? n_args? cu sublinierea. Ar trebui să aibă toate sublinierea. Credeam că sintaxa părea puțin amuzantă până când mi-am dat seama că au scăpat underscore în unele locuri.

0
adăugat

O altă opțiune ar fi modificarea structurii chess_move în locul prototipului funcției. Structura este probabil definită într-un singur loc deja. Adăugați membrii la structură și completați structura cu datele corespunzătoare înainte de orice apel care o folosește.

0
adăugat

Dacă citesc acest drept, ceea ce aș sugera este să-ți faci funcția să ia un pointer la un struct ca argument. Apoi, structul dvs. poate avea "joc" și "adâncime" atunci când are nevoie de ele, și doar lăsați-le setat la 0 sau Null atunci când nu aveți nevoie de ele.

Ce se întâmplă în această funcție? Aveți o condiție care spune:

if (depth > -1) //some default
  {
  //do something
  }

Are funcția ÎNDEPLINIRE "joc" și "adâncime"? Apoi, acestea ar trebui să fie întotdeauna argumente, care pot intra în prototipurile dvs.

Indicăm că funcția necesită uneori "joc" și "adâncime"? Ei bine, puteți face două funcții și utilizați fiecare câte o dată.

Dar, având o structură ca argument, este probabil cel mai ușor lucru.

0
adăugat