Разработка адаптера итератора для преобразования между не связанными объектами - PullRequest
0 голосов
/ 14 ноября 2018

У меня есть следующие MWE:

#include <iostream>
#include <vector>

using namespace std;

class LegacyWidgetData
{
  private:
    double _a;
    double _b;

  public:
    LegacyWidgetData()
      : _a(0), _b(0)
    {}

    LegacyWidgetData(const double &a, const double &b) 
      : _a(a), _b(b)
    {}

    LegacyWidgetData(const LegacyWidgetData& w)
      : _a(w.a()), _b(w.b())
    {}

    inline double &a()
    {
      return _a;
    }

    inline double a() const
    {
      return _a;
    }

    inline double &b()
    {
      return _b;
    }

    inline double b() const
    {
      return _b;
    }
};

template <std::size_t D>
class GenericWidgetData
{
private:
  double data[D];

public:
  GenericWidgetData(double a, double b)
  {
    data[0] = a;
    data[1] = b;
  }

  GenericWidgetData(double a, double b, double c)
  {
    data[0] = a;
    data[1] = b;
    data[2] = c;
  }

  double get(int idx)
  {
    return data[idx];
  }

  void set(int idx, const double& v)
  {
    data[idx] = v;
  }
};

template <typename Iterator>
void dummyFunction(Iterator begin, Iterator end)
{
  for (auto it = begin; it != end; it++)
  {
    cout << "Before: " << it->a() << "," << it->b() << "\t";
    it->a() += 1;
    it->b() -= 1;
    cout << "After: " << it->a() << "," << it->b() << "\n";
  }
}

int main()
{
  vector<LegacyWidgetData> c1{{1, 2}, {3, 4}, {5, 6}};
  dummyFunction(c1.begin(), c1.end());

  vector<GenericWidgetData<3>> c2{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

  // dummyFunction(c2.begin(), c2.end());  // Will not compile
  return 0;
}

У меня есть следующие предположения / ограничения:

  1. Я не могу изменить реализацию LegacyWidgetData или dummyFunction
  2. Я могу добавить методы к GenericWidgetData и добавить любые адаптеры итераторов, которые могут потребоваться.

То, что я хотел бы иметь, - это своего рода адаптер итератора, который при применении к любому виду итератора GenericWidgetData дает мне итератор, который действует как итератор LegacyWidgetData, без какого-либо кэширования / создания промежуточного вовлеченный объект Большой упор, если это можно сделать во время компиляции с использованием шаблонного метапрограммирования!

Ответы [ 2 ]

0 голосов
/ 14 ноября 2018

Если у вас есть адаптер итератора, то вам обычно требуется два таких объекта.

И если у вас много таких алгоритмов, то имеет смысл спрятать вектор внутри другого класса, чей метод begin / end вместо этого возвращает адаптеры.Что-то более или менее подобное:

templace <std::size_t D> class MyContainer
{
public:
    MyAdaptor<D> begin();
    MyAdaptor<D> end();
private:
    std::vector<GenericWidgetData<D>> data;
};

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

Тем не менее, лучшим решением, вероятно, было бы изменение 1000 функций таким образом, чтобывместо прямого доступа к членам LegacyWidgetData, тогда будет вызываться свободная функция.

double& b(LegacyWidgetData &data) { return data.b(); }
const double& b(const LegacyWidgetData &data) { return data.b(); }

И тогда вы добавите перегрузки для GenericWidgetData в зависимости от ситуации.Использование static_assert для проверки может работать относительно хорошо в этом случае:

template <std::size_t D> double& b(GenericWidgetData<D> &widgetData)
{
    static_assert(D > 1, "b is available only if D is big enough");
    return widgetData.data[D]; // Or an accessor function to avoid making it public
}

Затем вы добавили бы также вариант const.

Это больше работает, но более гибко.Например, вы можете правильно проверить размерность для каждой функции.

В качестве альтернативы, другое решение состоит в том, чтобы добавить дополнительные параметры в ваш алгоритм для применения преобразования.

template <typename Iterator, typename FA, typename FB>
void dummyFunction(Iterator begin, Iterator end, FA fa, FB fb)
{
  for (auto it = begin; it != end; it++)
  {
    cout << "Before: " << fa(*it) << "," << fb(*it) << "\t";
    fa(*it) += 1;
    fa(*it) -= 1;
    cout << "After: " << fa(*it) << "," << fb(*it) << "\n";
  }
}

Это может показатьсямного работы, но тогда вы очень гибкий.Поэтому, возможно, вы можете пересмотреть, какой код вы хотите изменить.

Возможно, было бы лучше сделать такое изменение сейчас и иметь более чистый код в будущем, чем создавать адаптер или добавлять множество функций к GenericWidgetData, которые предназначены для использования только алгоритмами.

0 голосов
/ 14 ноября 2018

Что вы можете сделать, это добавить специализацию шаблона к GenericWidgetData для случая, когда D == 2, а затем реализовать функции .a() и .b() для соответствия интерфейсу LegacyWidgetData.

Даже если бы можно было написать адаптер итератора, который мог бы переключаться во время компиляции с помощью некоторых приемов метапрограммирования, это было бы очень сложно понять любому, кроме его собственного автора.

...