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:

  1. 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.

  2. 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?

Était-ce utile?

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]

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top