Pregunta

Tengo una función para calcular el gradiente de diferentes variables definidas en el conjunto de puntos vecinos. El algoritmo es siempre el mismo, pero dependiendo de lo que se calcule, se accede a diferentes datos miembros de los vecinos, por ejemplo, al calcular gradiente de velocidad, usar Node::velocity, al calcular Gradiente de estrés, usar Node::stress. ¿Cuál es la mejor manera de evitar escribir la misma función varias veces?

Tenía varias posibilidades en mente:

  1. Pasar la función lambda (c ++ 0x) o el objeto llamable que devuelve esos datos de miembro en particular en cuestión, llamados como

    gradVelocity=computeGradient(listOfNeighbors,[](const Node& n){ return n.velocity; });
    

    La llamada MINUS es una llamada de función adicional en cada lectura.

  2. Plantaje la función basada en entero que dice lo que se está calculando:

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

    Quizás debería ser eficiente (con la esperanza de que el compilador optimice el condicional con constantes alejados en las instancias individuales), pero la legibilidad y la mantenibilidad son bastante bajas.

Una mejor idea?

¿Fue útil?

Solución

El puntero al miembro es lo que necesitas. El tipo está escrito como T S::* T es el tipo de miembro de datos, s es su estructura o clase. Aquí hay un pequeño ejemplo:

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

Otros consejos

Si todos sus campos tienen los mismos tipos, es fácil usar consejos para los miembros:

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

Actualizar: Si no, todavía es fácil combinar punteros a los miembros con plantillas:

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

Creo que esta es probablemente la forma más eficiente posible. También es bastante vívido.

Soy un gran admirador de Boost.fusion, y más específicamente, el Boost.fusion.map, que le permite construir un tipo de mapa tipo -> valor.

struct Velocity {};
struct Stress {};

typedef boost::fusion::map<
  std::pair<Velocity, double>,
  std::pair<Stress, int>
> Map;

Map map;

Ahora, puede acceder al mapa con tipos:

boost::fusion::at_key<Velocity>(map)

Devuelve una referencia a una variable de tipo boost::fusion::result_of::at_key<Velocity, Map>::type

Con el envoltorio apropiado, obtienes:

extern Velocity const velocity;
extern Stress const stress;

myItem.access(stress) = 3;

Y, por supuesto, ya que estamos hablando de plantillas, sin penalización de tiempo de ejecución, en absoluto :)

¿Qué hay de heredar de Node y usar acceso virtual? Incluso sería posible usar CRTP para evitar la llamada virtual.

Puedes combinar velocity, stress, something en una sola matriz y acceder a ellos según enum índice.

struct Node
{
  int attributes[3]; // contains 'velocity', 'stress', 'something';
  enum { VAL_VELOCITY=0, VAL_STRESS, VAL_SOMETHING };
};

Uso:

Node n;
n.attributes[Node::VAL_VELOCITY] = <value>;  // writing 'Node::velocity'
<otherthing> = n.attributes[Node::VAL_SOMETHING]; // reading 'Node::something'

Nota: si quieres mantener attributes en el interior private La región luego proporciona métodos de Getter y Setter en Node para acceder a ellos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top