Named, static dispatching with std::variant
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);
}
c++ c++17
add a comment |
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);
}
c++ c++17
Aside: isState
really meant to be a member ofVisitor
?
– Caleth
6 secs ago
add a comment |
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);
}
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);
}
c++ c++17
c++ c++17
edited 55 mins ago
Deduplicator
34k64787
34k64787
asked 1 hour ago
aerkenemesis
457312
457312
Aside: isState
really meant to be a member ofVisitor
?
– Caleth
6 secs ago
add a comment |
Aside: isState
really meant to be a member ofVisitor
?
– Caleth
6 secs ago
Aside: is
State
really meant to be a member of Visitor
?– Caleth
6 secs ago
Aside: is
State
really meant to be a member of Visitor
?– Caleth
6 secs ago
add a comment |
5 Answers
5
active
oldest
votes
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);
}
};
1
Priorizing onconst
... Now that's a clever trick I didn't see before. Neat!
– Quentin
29 mins ago
@Quentin Yep,const
makes it a worse conversion forVisitorProxy<Visitor>* this
. Alternatively,volatile
would also work.
– Maxim Egorushkin
21 mins ago
add a comment |
Using the now quite common overloaded
class template trick (And Maxim's trick to order the lambdas based on the const
ness 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 :)
1
Is it not UB to passEvent
into...
?
– Maxim Egorushkin
30 mins ago
1
@MaximEgorushkin I thought it was okay, but apparently it is implementation-defined... I'll pinch yourconst
-based ordering instead, much safer ;)
– Quentin
26 mins ago
add a comment |
If the Visitor can always apply, then the code can be as simple as
return std::visit([&](auto e) { return Visitor::apply(s, e); }, e);
But since Visitor cannot always apply, we need to use SFINAE, which requires a set of overloaded function templates. The function templates can be defined like this:
template<class EventType>
static auto applyHelper(State s, EventType e, int)
-> decltype(Visitor::apply(s, e)) // only enabled if Visitor::apply(s, e) is a valid expression
{
return Visitor::apply(s, e);
}
template<class EventType>
static State applyHelper(State s, EventType e, long) // long gives a lower precedence
// in overload resolution when argument is 0
{
return s;
}
Then the implementation of Applicator::apply
can be
static State apply(State s, Event e) {
return std::visit([&](auto e) { return applyHelper(s, e, 0); }, e);
}
add a comment |
Well, std::is_invocable_r
looks like the tool of choice.
Unfortunately, you would have to get the type of the right overload, which would completely defeat the purpose.
Instead, go one step back and use std::is_detected
from library fundamentals TS v2 or equivalent and a template:
template <class... Xs>
using can_Visitor_apply = decltype(Visitor::apply(std::declval<Xs>()...));
if constexpr(std::is_detected_convertible<State, can_Visitor_apply, State&, Event&>())
return Visitor::apply(s, e);
The advantage is that you have a compile-time-constant to hang arbitrary decisions on. The disadvantage is not (yet) having a function which you can simply just call and forget about it.
add a comment |
If I understand correctly, and you want write all without std::visit()
, your Applicator::apply()
can be written as follows
static typename Visitor::State apply(typename Visitor::State s, Event e) {
switch ( e.index() )
{
case 0:
return Visitor::apply(s, std::get<0>(e));
break;
case 1:
return Visitor::apply(s, std::get<1>(e));
break;
default:
return s;
}
}
But your code needs some correction (Visitor::State
or typename Visitor::State
instead of simply State
).
The following is a full compiling example
#include <variant>
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 Visitor::State apply(Visitor::State, EventA) { return Visitor::State{LastEvent::A}; }
static Visitor::State apply(Visitor::State, EventB) { return Visitor::State{LastEvent::B}; }
};
template <typename Visitor> struct Applicator {
static typename Visitor::State apply(typename Visitor::State s, Event e) {
switch ( e.index() )
{
case 0:
return Visitor::apply(s, std::get<0>(e));
break;
case 1:
return Visitor::apply(s, std::get<1>(e));
break;
default:
return s;
}
}
};
int main() {
using St = typename Visitor::State;
// Handled by visitor
St s1 = Applicator<Visitor>::apply(St{}, EventA{});
assert(s1.last_event == Visitor::LastEvent::A);
// Handled by visitor
St s2 = Applicator<Visitor>::apply(St{}, EventB{});
assert(s2.last_event == Visitor::LastEvent::B);
// NOT handled by visitor
St s3 = Applicator<Visitor>::apply(St{}, EventC{});
assert(s3.last_event == Visitor::LastEvent::None);
}
3
That's doing things the hard (as in manual and error-prone) way.
– Deduplicator
23 mins ago
@Deduplicator - I'm agree; but (if I understand correctly) is exactly what the OP is trying to do.
– max66
22 mins ago
There is now way of knowing how many alternativesEvent
has, hence this will not work
– aerkenemesis
7 mins ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54036439%2fnamed-static-dispatching-with-stdvariant%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
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);
}
};
1
Priorizing onconst
... Now that's a clever trick I didn't see before. Neat!
– Quentin
29 mins ago
@Quentin Yep,const
makes it a worse conversion forVisitorProxy<Visitor>* this
. Alternatively,volatile
would also work.
– Maxim Egorushkin
21 mins ago
add a comment |
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);
}
};
1
Priorizing onconst
... Now that's a clever trick I didn't see before. Neat!
– Quentin
29 mins ago
@Quentin Yep,const
makes it a worse conversion forVisitorProxy<Visitor>* this
. Alternatively,volatile
would also work.
– Maxim Egorushkin
21 mins ago
add a comment |
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);
}
};
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);
}
};
answered 32 mins ago
Maxim Egorushkin
85.3k1199182
85.3k1199182
1
Priorizing onconst
... Now that's a clever trick I didn't see before. Neat!
– Quentin
29 mins ago
@Quentin Yep,const
makes it a worse conversion forVisitorProxy<Visitor>* this
. Alternatively,volatile
would also work.
– Maxim Egorushkin
21 mins ago
add a comment |
1
Priorizing onconst
... Now that's a clever trick I didn't see before. Neat!
– Quentin
29 mins ago
@Quentin Yep,const
makes it a worse conversion forVisitorProxy<Visitor>* this
. Alternatively,volatile
would also work.
– Maxim Egorushkin
21 mins ago
1
1
Priorizing on
const
... Now that's a clever trick I didn't see before. Neat!– Quentin
29 mins ago
Priorizing on
const
... Now that's a clever trick I didn't see before. Neat!– Quentin
29 mins ago
@Quentin Yep,
const
makes it a worse conversion for VisitorProxy<Visitor>* this
. Alternatively, volatile
would also work.– Maxim Egorushkin
21 mins ago
@Quentin Yep,
const
makes it a worse conversion for VisitorProxy<Visitor>* this
. Alternatively, volatile
would also work.– Maxim Egorushkin
21 mins ago
add a comment |
Using the now quite common overloaded
class template trick (And Maxim's trick to order the lambdas based on the const
ness 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 :)
1
Is it not UB to passEvent
into...
?
– Maxim Egorushkin
30 mins ago
1
@MaximEgorushkin I thought it was okay, but apparently it is implementation-defined... I'll pinch yourconst
-based ordering instead, much safer ;)
– Quentin
26 mins ago
add a comment |
Using the now quite common overloaded
class template trick (And Maxim's trick to order the lambdas based on the const
ness 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 :)
1
Is it not UB to passEvent
into...
?
– Maxim Egorushkin
30 mins ago
1
@MaximEgorushkin I thought it was okay, but apparently it is implementation-defined... I'll pinch yourconst
-based ordering instead, much safer ;)
– Quentin
26 mins ago
add a comment |
Using the now quite common overloaded
class template trick (And Maxim's trick to order the lambdas based on the const
ness 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 :)
Using the now quite common overloaded
class template trick (And Maxim's trick to order the lambdas based on the const
ness 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 :)
edited 25 mins ago
answered 35 mins ago
Quentin
44.4k584140
44.4k584140
1
Is it not UB to passEvent
into...
?
– Maxim Egorushkin
30 mins ago
1
@MaximEgorushkin I thought it was okay, but apparently it is implementation-defined... I'll pinch yourconst
-based ordering instead, much safer ;)
– Quentin
26 mins ago
add a comment |
1
Is it not UB to passEvent
into...
?
– Maxim Egorushkin
30 mins ago
1
@MaximEgorushkin I thought it was okay, but apparently it is implementation-defined... I'll pinch yourconst
-based ordering instead, much safer ;)
– Quentin
26 mins ago
1
1
Is it not UB to pass
Event
into ...
?– Maxim Egorushkin
30 mins ago
Is it not UB to pass
Event
into ...
?– Maxim Egorushkin
30 mins ago
1
1
@MaximEgorushkin I thought it was okay, but apparently it is implementation-defined... I'll pinch your
const
-based ordering instead, much safer ;)– Quentin
26 mins ago
@MaximEgorushkin I thought it was okay, but apparently it is implementation-defined... I'll pinch your
const
-based ordering instead, much safer ;)– Quentin
26 mins ago
add a comment |
If the Visitor can always apply, then the code can be as simple as
return std::visit([&](auto e) { return Visitor::apply(s, e); }, e);
But since Visitor cannot always apply, we need to use SFINAE, which requires a set of overloaded function templates. The function templates can be defined like this:
template<class EventType>
static auto applyHelper(State s, EventType e, int)
-> decltype(Visitor::apply(s, e)) // only enabled if Visitor::apply(s, e) is a valid expression
{
return Visitor::apply(s, e);
}
template<class EventType>
static State applyHelper(State s, EventType e, long) // long gives a lower precedence
// in overload resolution when argument is 0
{
return s;
}
Then the implementation of Applicator::apply
can be
static State apply(State s, Event e) {
return std::visit([&](auto e) { return applyHelper(s, e, 0); }, e);
}
add a comment |
If the Visitor can always apply, then the code can be as simple as
return std::visit([&](auto e) { return Visitor::apply(s, e); }, e);
But since Visitor cannot always apply, we need to use SFINAE, which requires a set of overloaded function templates. The function templates can be defined like this:
template<class EventType>
static auto applyHelper(State s, EventType e, int)
-> decltype(Visitor::apply(s, e)) // only enabled if Visitor::apply(s, e) is a valid expression
{
return Visitor::apply(s, e);
}
template<class EventType>
static State applyHelper(State s, EventType e, long) // long gives a lower precedence
// in overload resolution when argument is 0
{
return s;
}
Then the implementation of Applicator::apply
can be
static State apply(State s, Event e) {
return std::visit([&](auto e) { return applyHelper(s, e, 0); }, e);
}
add a comment |
If the Visitor can always apply, then the code can be as simple as
return std::visit([&](auto e) { return Visitor::apply(s, e); }, e);
But since Visitor cannot always apply, we need to use SFINAE, which requires a set of overloaded function templates. The function templates can be defined like this:
template<class EventType>
static auto applyHelper(State s, EventType e, int)
-> decltype(Visitor::apply(s, e)) // only enabled if Visitor::apply(s, e) is a valid expression
{
return Visitor::apply(s, e);
}
template<class EventType>
static State applyHelper(State s, EventType e, long) // long gives a lower precedence
// in overload resolution when argument is 0
{
return s;
}
Then the implementation of Applicator::apply
can be
static State apply(State s, Event e) {
return std::visit([&](auto e) { return applyHelper(s, e, 0); }, e);
}
If the Visitor can always apply, then the code can be as simple as
return std::visit([&](auto e) { return Visitor::apply(s, e); }, e);
But since Visitor cannot always apply, we need to use SFINAE, which requires a set of overloaded function templates. The function templates can be defined like this:
template<class EventType>
static auto applyHelper(State s, EventType e, int)
-> decltype(Visitor::apply(s, e)) // only enabled if Visitor::apply(s, e) is a valid expression
{
return Visitor::apply(s, e);
}
template<class EventType>
static State applyHelper(State s, EventType e, long) // long gives a lower precedence
// in overload resolution when argument is 0
{
return s;
}
Then the implementation of Applicator::apply
can be
static State apply(State s, Event e) {
return std::visit([&](auto e) { return applyHelper(s, e, 0); }, e);
}
answered 26 mins ago
cpplearner
4,92421936
4,92421936
add a comment |
add a comment |
Well, std::is_invocable_r
looks like the tool of choice.
Unfortunately, you would have to get the type of the right overload, which would completely defeat the purpose.
Instead, go one step back and use std::is_detected
from library fundamentals TS v2 or equivalent and a template:
template <class... Xs>
using can_Visitor_apply = decltype(Visitor::apply(std::declval<Xs>()...));
if constexpr(std::is_detected_convertible<State, can_Visitor_apply, State&, Event&>())
return Visitor::apply(s, e);
The advantage is that you have a compile-time-constant to hang arbitrary decisions on. The disadvantage is not (yet) having a function which you can simply just call and forget about it.
add a comment |
Well, std::is_invocable_r
looks like the tool of choice.
Unfortunately, you would have to get the type of the right overload, which would completely defeat the purpose.
Instead, go one step back and use std::is_detected
from library fundamentals TS v2 or equivalent and a template:
template <class... Xs>
using can_Visitor_apply = decltype(Visitor::apply(std::declval<Xs>()...));
if constexpr(std::is_detected_convertible<State, can_Visitor_apply, State&, Event&>())
return Visitor::apply(s, e);
The advantage is that you have a compile-time-constant to hang arbitrary decisions on. The disadvantage is not (yet) having a function which you can simply just call and forget about it.
add a comment |
Well, std::is_invocable_r
looks like the tool of choice.
Unfortunately, you would have to get the type of the right overload, which would completely defeat the purpose.
Instead, go one step back and use std::is_detected
from library fundamentals TS v2 or equivalent and a template:
template <class... Xs>
using can_Visitor_apply = decltype(Visitor::apply(std::declval<Xs>()...));
if constexpr(std::is_detected_convertible<State, can_Visitor_apply, State&, Event&>())
return Visitor::apply(s, e);
The advantage is that you have a compile-time-constant to hang arbitrary decisions on. The disadvantage is not (yet) having a function which you can simply just call and forget about it.
Well, std::is_invocable_r
looks like the tool of choice.
Unfortunately, you would have to get the type of the right overload, which would completely defeat the purpose.
Instead, go one step back and use std::is_detected
from library fundamentals TS v2 or equivalent and a template:
template <class... Xs>
using can_Visitor_apply = decltype(Visitor::apply(std::declval<Xs>()...));
if constexpr(std::is_detected_convertible<State, can_Visitor_apply, State&, Event&>())
return Visitor::apply(s, e);
The advantage is that you have a compile-time-constant to hang arbitrary decisions on. The disadvantage is not (yet) having a function which you can simply just call and forget about it.
edited 22 mins ago
answered 28 mins ago
Deduplicator
34k64787
34k64787
add a comment |
add a comment |
If I understand correctly, and you want write all without std::visit()
, your Applicator::apply()
can be written as follows
static typename Visitor::State apply(typename Visitor::State s, Event e) {
switch ( e.index() )
{
case 0:
return Visitor::apply(s, std::get<0>(e));
break;
case 1:
return Visitor::apply(s, std::get<1>(e));
break;
default:
return s;
}
}
But your code needs some correction (Visitor::State
or typename Visitor::State
instead of simply State
).
The following is a full compiling example
#include <variant>
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 Visitor::State apply(Visitor::State, EventA) { return Visitor::State{LastEvent::A}; }
static Visitor::State apply(Visitor::State, EventB) { return Visitor::State{LastEvent::B}; }
};
template <typename Visitor> struct Applicator {
static typename Visitor::State apply(typename Visitor::State s, Event e) {
switch ( e.index() )
{
case 0:
return Visitor::apply(s, std::get<0>(e));
break;
case 1:
return Visitor::apply(s, std::get<1>(e));
break;
default:
return s;
}
}
};
int main() {
using St = typename Visitor::State;
// Handled by visitor
St s1 = Applicator<Visitor>::apply(St{}, EventA{});
assert(s1.last_event == Visitor::LastEvent::A);
// Handled by visitor
St s2 = Applicator<Visitor>::apply(St{}, EventB{});
assert(s2.last_event == Visitor::LastEvent::B);
// NOT handled by visitor
St s3 = Applicator<Visitor>::apply(St{}, EventC{});
assert(s3.last_event == Visitor::LastEvent::None);
}
3
That's doing things the hard (as in manual and error-prone) way.
– Deduplicator
23 mins ago
@Deduplicator - I'm agree; but (if I understand correctly) is exactly what the OP is trying to do.
– max66
22 mins ago
There is now way of knowing how many alternativesEvent
has, hence this will not work
– aerkenemesis
7 mins ago
add a comment |
If I understand correctly, and you want write all without std::visit()
, your Applicator::apply()
can be written as follows
static typename Visitor::State apply(typename Visitor::State s, Event e) {
switch ( e.index() )
{
case 0:
return Visitor::apply(s, std::get<0>(e));
break;
case 1:
return Visitor::apply(s, std::get<1>(e));
break;
default:
return s;
}
}
But your code needs some correction (Visitor::State
or typename Visitor::State
instead of simply State
).
The following is a full compiling example
#include <variant>
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 Visitor::State apply(Visitor::State, EventA) { return Visitor::State{LastEvent::A}; }
static Visitor::State apply(Visitor::State, EventB) { return Visitor::State{LastEvent::B}; }
};
template <typename Visitor> struct Applicator {
static typename Visitor::State apply(typename Visitor::State s, Event e) {
switch ( e.index() )
{
case 0:
return Visitor::apply(s, std::get<0>(e));
break;
case 1:
return Visitor::apply(s, std::get<1>(e));
break;
default:
return s;
}
}
};
int main() {
using St = typename Visitor::State;
// Handled by visitor
St s1 = Applicator<Visitor>::apply(St{}, EventA{});
assert(s1.last_event == Visitor::LastEvent::A);
// Handled by visitor
St s2 = Applicator<Visitor>::apply(St{}, EventB{});
assert(s2.last_event == Visitor::LastEvent::B);
// NOT handled by visitor
St s3 = Applicator<Visitor>::apply(St{}, EventC{});
assert(s3.last_event == Visitor::LastEvent::None);
}
3
That's doing things the hard (as in manual and error-prone) way.
– Deduplicator
23 mins ago
@Deduplicator - I'm agree; but (if I understand correctly) is exactly what the OP is trying to do.
– max66
22 mins ago
There is now way of knowing how many alternativesEvent
has, hence this will not work
– aerkenemesis
7 mins ago
add a comment |
If I understand correctly, and you want write all without std::visit()
, your Applicator::apply()
can be written as follows
static typename Visitor::State apply(typename Visitor::State s, Event e) {
switch ( e.index() )
{
case 0:
return Visitor::apply(s, std::get<0>(e));
break;
case 1:
return Visitor::apply(s, std::get<1>(e));
break;
default:
return s;
}
}
But your code needs some correction (Visitor::State
or typename Visitor::State
instead of simply State
).
The following is a full compiling example
#include <variant>
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 Visitor::State apply(Visitor::State, EventA) { return Visitor::State{LastEvent::A}; }
static Visitor::State apply(Visitor::State, EventB) { return Visitor::State{LastEvent::B}; }
};
template <typename Visitor> struct Applicator {
static typename Visitor::State apply(typename Visitor::State s, Event e) {
switch ( e.index() )
{
case 0:
return Visitor::apply(s, std::get<0>(e));
break;
case 1:
return Visitor::apply(s, std::get<1>(e));
break;
default:
return s;
}
}
};
int main() {
using St = typename Visitor::State;
// Handled by visitor
St s1 = Applicator<Visitor>::apply(St{}, EventA{});
assert(s1.last_event == Visitor::LastEvent::A);
// Handled by visitor
St s2 = Applicator<Visitor>::apply(St{}, EventB{});
assert(s2.last_event == Visitor::LastEvent::B);
// NOT handled by visitor
St s3 = Applicator<Visitor>::apply(St{}, EventC{});
assert(s3.last_event == Visitor::LastEvent::None);
}
If I understand correctly, and you want write all without std::visit()
, your Applicator::apply()
can be written as follows
static typename Visitor::State apply(typename Visitor::State s, Event e) {
switch ( e.index() )
{
case 0:
return Visitor::apply(s, std::get<0>(e));
break;
case 1:
return Visitor::apply(s, std::get<1>(e));
break;
default:
return s;
}
}
But your code needs some correction (Visitor::State
or typename Visitor::State
instead of simply State
).
The following is a full compiling example
#include <variant>
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 Visitor::State apply(Visitor::State, EventA) { return Visitor::State{LastEvent::A}; }
static Visitor::State apply(Visitor::State, EventB) { return Visitor::State{LastEvent::B}; }
};
template <typename Visitor> struct Applicator {
static typename Visitor::State apply(typename Visitor::State s, Event e) {
switch ( e.index() )
{
case 0:
return Visitor::apply(s, std::get<0>(e));
break;
case 1:
return Visitor::apply(s, std::get<1>(e));
break;
default:
return s;
}
}
};
int main() {
using St = typename Visitor::State;
// Handled by visitor
St s1 = Applicator<Visitor>::apply(St{}, EventA{});
assert(s1.last_event == Visitor::LastEvent::A);
// Handled by visitor
St s2 = Applicator<Visitor>::apply(St{}, EventB{});
assert(s2.last_event == Visitor::LastEvent::B);
// NOT handled by visitor
St s3 = Applicator<Visitor>::apply(St{}, EventC{});
assert(s3.last_event == Visitor::LastEvent::None);
}
edited 23 mins ago
answered 25 mins ago
max66
34.5k63762
34.5k63762
3
That's doing things the hard (as in manual and error-prone) way.
– Deduplicator
23 mins ago
@Deduplicator - I'm agree; but (if I understand correctly) is exactly what the OP is trying to do.
– max66
22 mins ago
There is now way of knowing how many alternativesEvent
has, hence this will not work
– aerkenemesis
7 mins ago
add a comment |
3
That's doing things the hard (as in manual and error-prone) way.
– Deduplicator
23 mins ago
@Deduplicator - I'm agree; but (if I understand correctly) is exactly what the OP is trying to do.
– max66
22 mins ago
There is now way of knowing how many alternativesEvent
has, hence this will not work
– aerkenemesis
7 mins ago
3
3
That's doing things the hard (as in manual and error-prone) way.
– Deduplicator
23 mins ago
That's doing things the hard (as in manual and error-prone) way.
– Deduplicator
23 mins ago
@Deduplicator - I'm agree; but (if I understand correctly) is exactly what the OP is trying to do.
– max66
22 mins ago
@Deduplicator - I'm agree; but (if I understand correctly) is exactly what the OP is trying to do.
– max66
22 mins ago
There is now way of knowing how many alternatives
Event
has, hence this will not work– aerkenemesis
7 mins ago
There is now way of knowing how many alternatives
Event
has, hence this will not work– aerkenemesis
7 mins ago
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54036439%2fnamed-static-dispatching-with-stdvariant%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Aside: is
State
really meant to be a member ofVisitor
?– Caleth
6 secs ago