2017-01-24 2 views
0

Je souhaite écrire une fonction qui utilise la valeur de retour d'une fonction double mais/et je souhaite choisir cette double fonction lors de l'appel de la fonction. Mon idée est la suivante:Appel funtion flexible avec void * et void **

#include <iostream> 

double linea(double a, double b, double x); 
double parabola(double a, double b, double c, double x); 

void konjgrad(double (*function)(void** params), void** params); 

double linea(double a, double b, double x){ 
    return a*x+b; 
} 

double parabola(double a, double b, double c, double x){ 
    return a*x*x+b*x+c; 
} 

void konjgrad(double (*function)(void** params), void** params){ 
    double d; 
    d=function(params); 
    std::cout<<d<<std::endl; 
} 

int main(){ 
    konjgrad(linea(1.6,5.1,2.6));  
    konjgrad(parabola(2.4,3.1,4,2.6)); 

    return 0; 
} 

Mais je suis resté dans le labyrinthe des pointeurs. Quelqu'un at-il une idée, comment résoudre le problème?

+0

Ne pas balises de spam. – StoryTeller

+0

Quoi qu'il en soit. Les solutions possibles vont de l'utilisation d'un modèle à l'effacement de type via 'std :: function'. Vous pouvez choisir celui que vous trouvez plus attrayant à vos besoins. – StoryTeller

+1

konjgrad (linéaires (1,6,5,1,2,6)); - vous ne passez pas un pointeur sur une fonction que vous appelez simplement la fonction et en lui transmettant le résultat – xaxxon

Répondre

4

Vous pouvez utiliser variadic templates et perfect-forwarding pour introduire la flexibilité dont vous avez besoin sans frais généraux d'exécution supplémentaire:

template <typename TF, typename... TArgs> 
void konjgrad(TF&& f, TArgs&&... args) 
{ 
    double d; 
    d = std::forward<TF>(f)(std::forward<TArgs>(args)...); 
    std::cout<<d<<std::endl; 
} 

konjgrad peut alors être appelé comme suit:

konjgrad(linea, 1.6, 5.1, 2.6);  
konjgrad(parabola, 2.4, 3.1, 4, 2.6); 

wandbox example


Sinon, vous pouvez utiliser lambda expressions et de mettre le fardeau de lier les arguments sur l'appelant:

template <typename TF> 
void konjgrad(TF&& f) 
{ 
    double d; 
    d = std::forward<TF>(f)(); 
    std::cout<<d<<std::endl; 
} 

konjgrad peut alors être appelé comme suit:

konjgrad([]{ return linea(1.6, 5.1, 2.6); });  
konjgrad([]{ return parabola(2.4, 3.1, 4, 2.6); }); 

wandbox example

+0

quel est le but de std :: move'ing la fonction? – xaxxon

+1

@xaxxon: Que se passe-t-il si 'f' n'a qu'une surcharge d'appel rvalue? –

+0

oh, c'est vrai, si c'est un foncteur. – xaxxon

0

Edité Based sur la révision de Vittorio Romeo, mise à jour pour passer les vecteurs comme référence const et ajouté l'assertion nécessaire à l'intérieur du linea et parabole APIs.

Voici une approche peu naïve si vous voulez éviter les fonctions variadiques. Comme votre interface de fonction est clairement définie, vous pouvez transmettre un vecteur ou un tableau de paramètres de type double dans vos fonctions linea et parabole.

#include <vector> 

using namespace std; 

double linea(const std::vector<double>& params) 
{ 
    assert(params.size() == 3 /*Linear 3 parameters required*/); 
    return params[0] * params[1] + params[2]; 
} 

double parabola(const std::vector<double>& params) 
{ 
    assert(params.size() == 4 /*Parabola 4 parameters required*/); 
    return params[0] * params[3] * params[3] + 
     params[1] * params[3] + 
     params[2]; 
} 

Un peu la version modifiée de votre konjgrad fonction, il retourne maintenant la valeur double de l'appel sous-jacent par le pointeur de fonction. Veuillez noter que le premier paramètre définit clairement l'interface de la fonction. Le deuxième paramètre est le vecteur des doubles.

double konjgrad(double(*function)(const std::vector<double>&), const std::vector<double>& params) 
{ 
    return function(params);//returning value from here 
} 

Enfin, voici comment vous devriez invoquer les API définies ci-dessus.

int main(int argc, wchar_t* argv[]) 
{ 
    std::vector<double> vals; 
    vals.push_back(1.6); 
    vals.push_back(5.1); 
    vals.push_back(2.6); 
    double v1 = konjgrad(linea, vals); 

    std::vector<double> parab; 
    parab.push_back(2.4); 
    parab.push_back(3.1); 
    parab.push_back(4.0); 
    parab.push_back(2.6); 

    double v2 = konjgrad(parabola, parab); 

    return 0; 

}

espoir que cela aiderait.

+0

J'ai downvoted cette solution car vous allouez inutilement de la mémoire et copiez les instances 'std :: vector'. Vous supposez également que les vecteurs auront la taille correcte sans «affirmer» - c'est dangereux! En outre, la syntaxe de l'appelant n'est pas agréable du tout. –

+0

Veuillez noter la toute première ligne de OP "Je souhaite écrire une fonction, qui utilise la valeur de retour d'une double fonction mais/et je veux choisir cette double fonction lors de l'appel de fonction", je pense que la réponse pour la question de l'OP. Deuxièmement, je comprends votre inquiétude que le vecteur ne soit pas transmis comme référence (et crée une copie inutile sur la pile), je voulais juste partager l'idée, alors devrais-je mettre à jour le code alors? et ajouter une assertion, etc. –