определить версию API через sfinae - PullRequest
0 голосов
/ 03 июля 2018

Я работаю над базой кода, где C ++ API собирается измениться (основная ветвь и функциональная ветвь), и я хочу защитить свой код от изменений, чтобы мой код создавался для обеих версий. Как минимальный рабочий пример godbolt / ссылка компилятора-проводника :

общая:

class SmallWidget {
 private:
  int m_i;

 public:
  int i() { return m_i; }
};

мастер:

class BigWidget {
 private:
  std::vector<SmallWidget*> m_v;

 public:
  auto& widgets() { return m_v; }
};

ветвь функции:

class BigWidget {
 private:
  std::vector<SmallWidget> m_v;

 public:
  auto& widgets() { return m_v; }
};
/**/

Для мастер-версии у меня есть код вызова:

int single_op(BigWidget& w) {
  return (*w.widgets().begin())->i(); // asterisk will need to go
}


std::vector<int> do_stuff(std::vector<BigWidget> inputs) {
  std::vector<int> retval;
  for (auto w : inputs) {
    retval.push_back(single_op(w));
  }

  return retval;
}

API не поставляется с переменной препроцессора для выбора с помощью

#if WIDGET_VERSION == OLD
  return (*w.widgets().begin())->i(); // asterisk will need to go
#else
  return (w.widgets().begin())->i(); // asterisk gone
#endif

Я пытался определить тип возвращаемого значения widgets с помощью sfinae:

// version for the master branch API
template <typename = typename std::enable_if<std::is_same<
              std::remove_const<std::remove_reference<decltype(**(
                  std::declval<BigWidget>().widgets().begin()))>::type>::type,
              SmallWidget>::value>::type>
int single_op(BigWidget& w) {
  return (*w.widgets().begin())->i();
}

// version for the feature branch API
template <typename = typename std::enable_if<std::is_same<
              std::remove_const<std::remove_reference<decltype(*(
                  std::declval<BigWidget>().widgets().begin()))>::type>::type,
              SmallWidget>::value>::type>
int single_op(BigWidget& w) { return w.widgets().begin()->i(); }

Но компиляторы этому не рады. лязг говорит:

<source>:43:46: error: failed requirement 'std::is_same<std::remove_const<std::remove_reference<decltype(* (std::declval<BigWidget>().widgets().begin()))>::type>::type, SmallWidget>::value'; 'enable_if' cannot be used to disable this declaration

шаблон

Есть ли способ использовать sfinae для включения правильного кода здесь?

Я также пытался использовать std::enable_if для кода возврата или аргументов функции, но, насколько я понимаю, компилятор всегда видит сигнатуру int single_op(BigWidget&) для обеих версий.

Ответы [ 2 ]

0 голосов
/ 03 июля 2018

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

SmallWidget &getWidget(SmallWidget *w) { return *w; }
SmallWidget &getWidget(SmallWidget &w) { return w; }

int single_op(BigWidget &w) {
    return getWidget(*w.widgets().begin()).i();
}

Вы также можете предоставить перегрузки для const версий.

0 голосов
/ 03 июля 2018

Я мог бы придумать эту версию:

// version for the feature branch API
template <typename bigtype>
int single_op(
     typename std::enable_if<
         std::is_same<
             std::remove_const<std::remove_reference<decltype(
                *(std::declval<BigWidget>().widgets().begin()))>::type>::type,
            SmallWidget>::value,
        bigtype>::type& w) {
  return w.widgets().begin()->i();
}

Я добавил один дополнительный аргумент шаблона для типа BigWidget (несмотря на то, что он известен) и использую его для аргумента функции single_op. Теперь компилятор может пройти оба определения шаблона, получить одно возможное решение для набора перегрузки и отключить неиспользуемый код.

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

retval.push_back(single_op<decltype(w)>(w));

Но я подозреваю, что есть лучшие решения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...