Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Named, static dispatching with std::variant

Tags:

c++

c++17

I need to fill in some template magic to make the following code snippet to work.

The problem is that I want to be able to define a visitor class for std::variant with named static methods accepting two arguments. How can I fill in Applicator::apply() to make the dispatching work?

struct EventA {};

struct EventB {};

struct EventC {};

using Event = std::variant<EventA, EventB, EventC>;

struct Visitor {
  enum class LastEvent { None, A, B, C };

  struct State {
    LastEvent last_event = LastEvent::None;
  };

  static State apply(State s, EventA e) { return State{LastEvent::A}; }

  static State apply(State s, EventB e) { return State{LastEvent::B}; }
};

template <typename Visitor> struct Applicator {

  static State apply(State s, Event e) {

    /*** Start of pseudo code ***/
    if (Visitor can apply) {
      return Visitor::apply(s, e);
    }
    /*** End of pseudo code ***/

    // Else, don't update state state
    return s;
  }
};

int main() {
  // Handled by visitor
  State s1 = Applicator<Visitor>::apply(State{}, EventA{});
  assert(s1.last_event == Visitor::LastEvent::A);

  // Handled by visitor
  State s2 = Applicator<Visitor>::apply(State{}, EventB{});
  assert(s2.last_event == Visitor::LastEvent::B);

  // NOT handled by visitor
  State s3 = Applicator<Visitor>::apply(State{}, EventC{});
  assert(s3.last_event == Visitor::LastEvent::None);
}
like image 813
aerkenemesis Avatar asked Jan 04 '19 09:01

aerkenemesis


People also ask

Does std :: variant allocate?

One additional thing is that std::variant does not dynamically allocate memory for the values. Any instance of std::variant at any given time either holds a value of one of its alternative types, or it holds no value at all.

Can std :: variant be empty?

Empty variants are also ill-formed (std::variant<std::monostate> can be used instead). A variant is permitted to hold the same type more than once, and to hold differently cv-qualified versions of the same type.

What is STD Monostate?

std::monostate is a value-semantic type, like bool — it just has one fewer value in its domain. It can be stored in variants, or containers, or set s (it's ordered!), or unordered_set s (it's hashable!), or anywhere else you might use a value-semantic type like bool .

What is boost :: variant?

Boost. Variant, part of collection of the Boost C++ Libraries. It is a safe, generic, stack-based discriminated union container, offering a simple solution for manipulating an object from a heterogeneous set of types in a uniform manner.


2 Answers

Another solution:

using State = Visitor::State;

template<class Visitor>
struct VisitorProxy {
    State s;

    template<class E>
    auto operator()(E const& e) -> decltype(Visitor::apply(s, e)) {
        return Visitor::apply(s, e);
    }

    template<class E>
    State operator()(E const&) const {
        return s;
    }
};

template <typename Visitor> struct Applicator {
    static State apply(State s, Event e) {
        VisitorProxy<Visitor> p{s};
        return std::visit(p, e);
    }
};
like image 138
Maxim Egorushkin Avatar answered Oct 28 '22 14:10

Maxim Egorushkin


Using the now quite common overloaded class template trick (And Maxim's trick to order the lambdas based on the constness of their operator()) to create a SFINAE-capable functor modeling the logic you're lookig for:

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

// ...

template <typename Visitor> struct Applicator {
  static typename Visitor::State apply(typename Visitor::State s, Event e) {
    return std::visit(overloaded{
      [&s](auto e) mutable -> decltype(Visitor::apply(s, e)) { return Visitor::apply(s, e); },
      [&s](auto) { return s; }
    }, e);
  }
};

Note that this ICEs all versions of Clang I've tested on Wandbox, but I haven't found a workaround. Perfect forwarding is left as an exercise to the reader :)

like image 28
Quentin Avatar answered Oct 28 '22 13:10

Quentin