шаблон для доступа к различным членам объектов, передаваемых в качестве аргументов - PullRequest
1 голос
/ 06 сентября 2011

У меня есть функция для вычисления градиента другой переменной, определенной на множестве соседних точек. Алгоритм всегда один и тот же, но в зависимости от того, что вычисляется, доступ к различным данным члена соседей, например, при расчете градиент скорости , используйте Node::velocity, при расчете градиент напряжения , используйте Node::stress. Каков наилучший способ избежать написания одной и той же функции несколько раз?

Я имел в виду несколько возможностей:

  1. Передача лямбда-функции (c ++ 0x) или вызываемый объект, возвращающий данные конкретного члена, который называется

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

    Минус - дополнительный вызов функции при каждом чтении.

  2. Шаблон функции на основе целого числа, говорящего о том, что вычисляется:

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

    Возможно, он должен быть эффективным (надеясь, что компилятор оптимизирует условные выражения с помощью констант в отдельных экземплярах), но читаемость и удобство сопровождения довольно низкие.

Есть идея получше?

Ответы [ 5 ]

5 голосов
/ 06 сентября 2011

Если все ваши поля имеют одинаковые типы, легко использовать указатели на элементы:

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

Обновление: Если нет, по-прежнему легко комбинировать указатели на элементы с помощью шаблонов:

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

Я думаю, что это, вероятно, самый эффективный способ. Это тоже довольно ярко.

2 голосов
/ 06 сентября 2011

Указатель на пользователя - это то, что вам нужно. Тип записывается как T S::* T - тип члена данных, S - ваша структура или класс. Вот небольшой пример:

#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);
}
1 голос
/ 06 сентября 2011

Я большой поклонник Boost.Fusion, а точнее, Boost.Fusion.Map , который позволяет вам строить карту типа -> значение.

struct Velocity {};
struct Stress {};

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

Map map;

Теперь вы можете получить доступ к карте с типами:

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

возвращает ссылку на переменную типа boost::fusion::result_of::at_key<Velocity, Map>::type

При соответствующем переносе вы получаете:

extern Velocity const velocity;
extern Stress const stress;

myItem.access(stress) = 3;

И, конечно же, поскольку мы говорим о шаблонах, никаких штрафов за время выполнения вообще не будет:)

0 голосов
/ 06 сентября 2011

Вы можете объединить velocity, stress, something в одном массиве и получать к ним доступ на основе индекса enum.

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

Использование:

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

[Примечание: Если вы хотите сохранить attributes внутри private региона, предоставьте методы получения и установки в Node для доступа к ним.]

0 голосов
/ 06 сентября 2011

Как насчет наследования от Node и использования виртуального доступа? Можно было бы даже использовать CRTP, чтобы избежать виртуального вызова.

...