Спросите во время выполнения, если альтернатива в варианте удовлетворяет определенной концепции - PullRequest
0 голосов
/ 30 января 2019

У меня есть следующий код , где я пытаюсь определить, какая концепция (в данном случае функции-члены) имеет альтернативу в варианте.Это многословно и относительно некрасиво.Есть ли лучший способ сделать это?Обратите внимание, что я не хочу использовать наследование и не хочу использовать статический полиморфизм (предположим, что используемый движок неизвестен во время компиляции).

Проще говоря, я ищу смесь во время выполненияif constexpr (который проверяет только концепции во время компиляции) и std::holds_alternative (который может только проверять, находится ли конкретный тип в variant, а не, если какой-либо из типов, удовлетворяющих концепции, находится в variant).

#include <iostream>
#include <variant>


struct simple_engine1{

};
struct simple_engine2{

};
struct complex_engine1{
    void reduce_thrust(int perc){
        std::cout<<"reducing thrust " << perc << "% " << std::endl;
    }
};
struct complex_engine2{
    void reduce_thrust(int perc){
        std::cout<<"reducing thrust " << perc << "% " << std::endl;
    }
};
template< class, class = std::void_t<> >
struct has_reduce_thrust : std::false_type { };
template< class T >
struct has_reduce_thrust<T,
           std::void_t<decltype( std::declval<T>().reduce_thrust(42) )>
       > : std::true_type { };
static_assert(!has_reduce_thrust<simple_engine1>::value);
static_assert(!has_reduce_thrust<simple_engine1>::value);
static_assert(has_reduce_thrust<complex_engine1>::value);

struct visitor{
  template<typename T>
  void operator()(T& t){
      dispatch(t, has_reduce_thrust<T>{});

  }
  template<typename T>
  void dispatch(T& t, std::true_type /*has_reduce_thrust*/){
      t.reduce_thrust(perc);
      reduced_thrust=true;
  }
  template<typename T>
  void dispatch(T& , std::false_type){
      reduced_thrust=false;
  }
  int perc = 0;
  bool reduced_thrust = false;  
};
// tries to reduce speed by reducing thrust if engine supports it, if not
// it fires reverse engines(more expensive wrt fuel usage)
void reduce_speed(std::variant<simple_engine1, simple_engine2, complex_engine1, complex_engine2>* var_engine){
    visitor v;
    v.perc = 47;
    std::visit(v, *var_engine);
    if (v.reduced_thrust) {
        std::cout << "reduced thrust\n"; 
    } else {
        std::cout << "activating reverse engines\n";
    }
}
int main() {
    std::variant<simple_engine1, simple_engine2, complex_engine1, complex_engine2> var_engine{simple_engine1{}};
    reduce_speed(&var_engine);
    var_engine = complex_engine2{};
    reduce_speed(&var_engine);
    var_engine = simple_engine2{};
    reduce_speed(&var_engine);
    var_engine = complex_engine2{};
    reduce_speed(&var_engine);
} 

1 Ответ

0 голосов
/ 30 января 2019

Вы можете значительно упростить посетителя, используя if constexpr:

struct visitor{
  template<typename T>
  void operator()(T& t) {
    if constexpr (has_reduce_thrust<T>::value) {
      t.reduce_thrust(perc);
      reduced_thrust = true;
    }
    else {
      reduced_thrust = false;
    }
  }

  int perc = 0;
  bool reduced_thrust = false;  
};

Затем вы можете абстрагироваться дальше, приняв любой предикат и две функции для любой ветви if constexpr:

template <template <class, class... /*SFINAE friendly*/> class TypePred,
          class MatchedFunc, class UnmatchedFunc>
class predicated_visitor {
 public:
  predicated_visitor(MatchedFunc matchedFunc, UnmatchedFunc unmatchedFunc)
      : _matchedFunc(matchedFunc), _unmatchedFunc(unmatchedFunc) {}

  template <typename T>
  void operator()(T& t) {
    if constexpr (TypePred<T>::value)
      _matchedFunc(t);
    else
      _unmatchedFunc(t);
  }

 private:
  MatchedFunc _matchedFunc;
  UnmatchedFunc _unmatchedFunc;
};

template <template <class, class... /*SFINAE friendly*/> class TypePred,
          class F1, class F2>
auto makePredicatedVisitor(F1 f1, F2 f2) {
  return predicated_visitor<TypePred, F1, F2>(f1, f2);
}

Полученный код довольно приятный, я чувствую:

void reduce_speed(std::variant<simple_engine1, simple_engine2, complex_engine1,
                               complex_engine2>* var_engine) {
  int perc = 47;
  bool reducedThrust = false;

  auto reduceableThrustAction = [perc, &reducedThrust](auto& t) {
    t.reduce_thrust(perc);
    reducedThrust = true;
  };
  auto alternativeAction = [](auto& t) {
  };  // Could explicitly set reduceThrust to false for clarity.

  auto thrust_visitor = makePredicatedVisitor<has_reduce_thrust>(
      reduceableThrustAction, alternativeAction);

  std::visit(thrust_visitor, *var_engine);

  if (reducedThrust) {
    std::cout << "reduced thrust\n";
  } else {
    std::cout << "activating reverse engines\n";
  }
}

Демонстрация

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

Прошу прощения за непоследовательную капитализацию ...

...