modèle d'accès différents membres d'objets passés comme arguments
-
26-10-2019 - |
Question
I ai une fonction pour calculer un gradient de la variable différente définie sur ensemble des points voisins. L'algorithme est toujours le même, mais en fonction de ce qui est calculée, différentes données membres des voisins sont accessibles, par exemple lors du calcul gradient de vitesse , l'utilisation Node::velocity
, lors du calcul gradient de contrainte , l'utilisation Node::stress
. Quelle est la meilleure façon d'éviter d'écrire la même fonction à plusieurs reprises?
J'ai eu plusieurs possibilités à l'esprit:
-
Col ??fonction de lambda (c ++ 0x) ou objet appelable retour que les données membres particuliers en question, appelé comme
gradVelocity=computeGradient(listOfNeighbors,[](const Node& n){ return n.velocity; });
Le moins est appel de fonction supplémentaire à chaque lecture.
-
Modèle de la fonction basée sur entier en disant ce qui est calculé:
enum{VAL_VELOCITY=0,VAL_STRESS,VAL_SOMETHING}; template<int what> computeGradient(const std::list<Node>& neighbors){ /*loop over neighbors*/ value=(what==VAL_VELOCITY?neighbor.velocity:((what==VAL_STRESS)?neighbor.stress:neighbor.something); /* and so on */ } /* called like this */ gradVelocity=computeGradient<VAL_VELOCITY>(neighbors);
Il doit être peut-être efficace (compilateur espérant optimisera le conditionnel avec des constantes loin dans instanciations individuelles), mais la lisibilité et la maintenabilité est assez faible.
Certains meilleure idée?
La solution
pointeur vers un membre est ce que vous avez besoin. Le type est écrit T S::*
T est le type de l'élément de données, S est votre struct ou une classe. Voici un petit exemple:
#include <iostream>
struct Foo
{
int a;
double b;
Foo(int a, double b)
: a(a), b(b)
{ }
};
template<typename T, T Foo::* mem>
void print(const Foo& foo)
{
std::cout << foo.*mem << std::endl;
}
int main()
{
Foo f(5, 3.14);
print<int, &Foo::a>(f);
print<double, &Foo::b>(f);
}
Autres conseils
Si tous vos champs ont les mêmes types, il est facile de utiliser des pointeurs aux membres:
struct Node
{
double stress;
double velosity;
};
void foo(Node* pNode, double Node::*pValue)
{
cout << pNode->*pValue << endl;
}
int main()
{
Node n1 = { 1, 2 };
foo(&n1, &Node::stress);
foo(&n1, &Node::velosity);
}
Mise à jour: Sinon, il est toujours facile de combiner des pointeurs aux membres des modèles:
struct Node
{
double stress;
double velosity;
int dimension;
};
template<class T>
void foo(Node* pNode, T Node::*pValue)
{
cout << pNode->*pValue << endl;
}
int main()
{
Node n1 = { 1, 2 };
foo(&n1, &Node::stress);
foo(&n1, &Node::velosity);
foo(&n1, &Node::dimension);
}
Je pense que ce qui est probablement le moyen le plus efficace possible. Il est assez trop vive.
Je suis un grand fan de Boost.Fusion, et plus précisément, le Boost.Fusion.Map , qui vous permettent de construire un type -.> valeur type de carte
struct Velocity {};
struct Stress {};
typedef boost::fusion::map<
std::pair<Velocity, double>,
std::pair<Stress, int>
> Map;
Map map;
Maintenant, vous pouvez accéder à la carte avec des types:
boost::fusion::at_key<Velocity>(map)
renvoie une référence à une variable de type boost::fusion::result_of::at_key<Velocity, Map>::type
Avec emballage approprié, vous obtenez:
extern Velocity const velocity;
extern Stress const stress;
myItem.access(stress) = 3;
Et bien sûr, puisque nous parlons des modèles, pas de pénalité d'exécution, à tous:)
Qu'en héritant de Node
et en utilisant un accès virtuel? Il serait même possible d'utiliser CRTP pour éviter l'appel virtuel.
Vous pouvez combiner velocity
, stress
, something
dans un seul tableau et l'accès basé sur les index enum
.
struct Node
{
int attributes[3]; // contains 'velocity', 'stress', 'something';
enum { VAL_VELOCITY=0, VAL_STRESS, VAL_SOMETHING };
};
Utilisation:
Node n;
n.attributes[Node::VAL_VELOCITY] = <value>; // writing 'Node::velocity'
<otherthing> = n.attributes[Node::VAL_SOMETHING]; // reading 'Node::something'
[Note:. Si vous voulez garder attributes
dans la région de private
puis fournir des méthodes getter et setter dans Node
pour y accéder]