2010-06-02 13 views
2

Je construis une partie d'un simulateur. Nous construisons à partir d'un simulateur de legs, mais allant dans la direction différente, incorporant des morceaux vivants le long du côté des morceaux simulés. La pièce sur laquelle je travaille doit effectivement acheminer les commandes du contrôleur central vers les différents bits.Et maintenant pour un changement complet de direction des pointeurs de fonction C++

Dans le code hérité, il existe un tableau const rempli avec un type énuméré. Une commande arrive, elle est recherchée dans la table, puis expédiée vers une instruction de commutateur avec le type énuméré. L'énumération de type a un choix VALID_BUT_NOT_SIMULATED, qui est effectivement un no-op du point de la simulation. J'ai besoin de transformer ces no-ops en commandes à d'autres choses réelles [nouveaux bits simulés | live bits]. Les nouveaux trucs et les trucs en direct ont des interfaces différentes que les vieux trucs [ce qui me fait rire au sujet du travail de shill qu'il a fallu pour que tout cela arrive, mais c'est un sujet pour une discussion différente]. J'aime le tableau parce que c'est une description très juste du truc en direct que ce morceau simule [circuits de verrouillage par ligne et colonne]. J'ai pensé que j'essaierais de remplacer les types énumérés dans le tableau par des pointeurs vers des fonctions et de les appeler directement. Ce serait à la place de la recherche + commutateur.

+10

Comment avez-vous l'intention d'appeler ces fonctions via le pointeur de rappel? –

+4

Vous avez décrit une solution technique impossible - essayez de décrire le problème réel. –

+0

C'est une bonne question qui est probablement saillant. Je vais mettre à jour ma question parce que ça va prendre un peu de place. – David

Répondre

3

Impossible à faire. Cependant, vous pourriez faire quelque chose comme ça avec un foncteur. Je mettrais un exemple de code mais en l'écrivant je me suis rendu compte qu'une telle construction serait nécessairement assez compliquée. Vous pourriez regarder boost :: bind pour quelques idées.

2

Une façon de le faire, si laid, est d'utiliser une table générique de pointeurs et jeter vos pointeurs de fonction pour ce type générique (perte d'informations sur les types des arguments):

void (*myFunctions[])() = { 
    (void (*)())myFirstFunction, 
    (void (*)())mySecondFunction 
}; 

Mais vous Il va falloir savoir, pour chacun des pointeurs, quels arguments passer aux fonctions correspondantes. Vous pouvez étendre votre table de pointeurs et créer une table d'objets plus sophistiqués qui contiennent une variable d'énumération renseignant sur les arguments d'une fonction vers laquelle pointe un pointeur particulier.

Malheureusement, chaque fois que vous voudrez utiliser une fonction de la matrice, vous aurez besoin de renvoyer le pointeur au type donné (et vous ne devrez pas le lancer incorrectement), comme ceci:

((void (*)(int))tab[0])(1); 

pour appeler myFirstFunction avec x = 1.

Comme je pense maintenant (après avoir changé la question), je viens à la conclusion que si vous devez appeler les fonctions différemment, il n'y a vraiment aucun point compliquant le tout (table de recherche), à ​​moins qu'il y ait seulement quelques signatures et beaucoup de fonctions disponibles. Vous avez besoin d'une politique d'appel très cohérente et de très peu de signatures possibles pour obtenir une bonne solution avec une table de recherche. Inutile de mentionner ce qui se passera si vous avez besoin de stocker des pointeurs vers des fonctions membres ou pire encore - des virtuals.

+0

A ce stade, vous pourriez aussi bien appeler les fonctions directement! –

+0

Je ne fais que répéter ce que vous avez dit en premier: c'est moche. –

+0

J'ai complètement changé ma question, ce qui peut être mauvaise étiquette, en réponse à Neil, mais je veux dire "Merci", et "Wow, je suppose qu'avec une réponse comme ça, je reçois ce que je mérite." – David

1

En fonction de votre question mise à jour, je ne sais toujours pas comment vous allez appeler les fonctions via le pointeur si les fonctions nécessitent des listes de paramètres différentes.

Cependant, si une liste de paramètres est un sous-ensemble de l'autre, pourriez-vous écrire des thunks pour adapter une interface à l'autre? (c'est-à-dire supprimer des paramètres non pertinents ou synthétiser de faux paramètres).

+0

À la fin, j'ai estimé que j'aurais quelque chose comme: void (* commandArray [])() = {myFirstFunction (1), myFirstFunction (2), mySecondFunction (3,4)}; Ensuite, je ferais l'appel par: commandArray [commandIndex] où commandIndex a été transmis à partir d'ailleurs. – David

+1

@David: Basé sur cela, je suggère d'aller avec la solution de thunk. –

+0

J'apprécie votre aide. Vos commentaires m'ont éclairci sur le problème. Je vais regarder les thunks et voir ce que je peux en tirer. – David

0

Dans votre question initiale, vous décriviez un scénario très courant lorsque vous travaillez avec des bibliothèques Javascript.En Javascript, les bibliothèques fournissent souvent un moyen pour les «parties intéressées» d'être averties des événements qui sont publiés par la bibliothèque, et tout ce que les parties intéressées doivent faire est d'enregistrer leurs propres rappels d'objet Function. Dans une version de la bibliothèque, la documentation pourrait dire que les callbacks seront transmis n arguments (a, b, c, ... dans cet ordre), mais une future version pourrait vouloir fournir n + m arguments. Cette modification ne doit pas casser le code existant, car la bibliothèque peut simplement ajouter les arguments supplémentaires m à la liste des arguments, et cela fonctionne parce que Javascript utilise une convention d'appel caller-cleans-up (essentiellement).

En C++, vous pouvez faire quelque chose de similaire (fournir supplémentaires arguments à callbacks) aussi longtemps que vous pouvez garantir que la convention d'appel qui est utilisé par les callbacks et d'appeler les callbacks est un appelant assainit-up d'appel convention telle que C calling convention for the x86 architecture:

#include <cstdlib> 
#include <iostream> 
#include <vector> 

extern "C" void old_api_callback_in_old_archive(int x) { 
    std::cout << "`old_api_callback_in_old_archive` was called with x = " << x << std::endl; 
} 

extern "C" void new_api_callback(int x, int otherInfo) { 
    std::cout << "`new_api_callback` was called with x = " << x 
     << ", otherInfo = " << otherInfo << std::endl; 
} 

extern "C" { 
    typedef void (*callback_type)(int, int); 
} 

int main() 
{ 
    std::vector<callback_type> callbacks; 
    callbacks.push_back(&new_api_callback); 
    callbacks.push_back(reinterpret_cast<callback_type>(&old_api_callback_in_old_archive)); 

    std::vector<callback_type>::iterator it; 
    for (it = callbacks.begin(); it != callbacks.end(); ++it) { 
     (*it)(7, -8); 
    } 

    return EXIT_SUCCESS; 
}