Info
-
Did you know about C++23 feature which adds support for inheriting from std::variant?
Example
struct value : std::variant<int, double> {
using variant::variant;
constexpr auto operator()(const auto& value) {
std::clog << value << '\n';
}
};
int main(){
std::visit(value{}, std::variant<int, double>(42)); // prints 42
std::visit(value{}, std::variant<int, double>(99.)); // prints 99
}
Puzzle
- Can you implement a generic state variant with
id
API (returns true if the given type is hold) and print_id which prints state id if available?
template<class... Ts>
class state; // TODO
const auto print_id = [] (auto& os); // TODO
int main(){
using namespace boost::ut;
struct disconnected {};
struct connected { int id{}; };
"inheriting from variant"_test = [] {
state<disconnected, connected> state{};
should("be disconnected and print nothing") = [=] {
std::stringstream str{};
mut(state) = disconnected{};
std::visit(print_id(str), state);
expect(state.is<disconnected>());
expect(std::string{""} == str.str());
};
should("be connected and print id") = [=] {
std::stringstream str{};
mut(state) = connected{.id = 42};
std::visit(print_id(str), state);
expect(state.is<connected>());
expect(std::string{"42"} == str.str());
};
};
}
Solutions
template<class... Ts>
struct state : std::variant<Ts...> {
template<class T>
state& operator=(T&& t) noexcept {
return (*this = state{t});
}
template<class T>
bool is() const {
return std::holds_alternative<T>(*this);
}
};
const auto print_id = [] (auto& os) {
return [&os] (const auto& st) {
if constexpr (requires { st.id; }) {
os << st.id;
} else {
os << "";
}
};
};
template <class... Ts>
struct state : std::variant<Ts...> {
using std::variant<Ts...>::operator=;
template <typename T>
[[nodiscard]] constexpr auto is() const noexcept -> bool {
return std::holds_alternative<T>(*this);
}
};
const auto print_id = [](auto& os) {
return [&](const auto state) {
if constexpr (requires { state.id; }) {
os << state.id;
}
};
};
template<class... Ts>
class state : public std::variant<Ts...> {
using base = std::variant<Ts...>;
using base::base;
public:
template <typename T>
constexpr bool is() const { return holds_alternative<T>(*this); }
};
const auto print_id = [] (auto& os) {
return [&] (const auto& member) {
if constexpr (requires { member.id; }) {
os << member.id;
}
};
};
template<class... Ts>
struct state : std::variant<Ts...> {
using std::variant<Ts...>::variant;
template <typename T>
constexpr auto is() const noexcept -> bool {
return std::holds_alternative<T>(*this);
}
};
const auto print_id = [] (auto& os) {
return [&] (auto const& state) {
if constexpr (requires { state.id; }) {
os << state.id;
}
};
};
template<class... Ts>
class state : public std::variant<Ts...> {
public:
using std::variant<Ts...>::variant;
template <class T>
constexpr auto is() const {
return std::holds_alternative<T>(*this);
}
};
const auto print_id = [] ([[maybe_unused]] auto& os) {
return [&] (const auto& v) {
if constexpr (requires { os << v.id; }) {
os << v.id;
}
};
};
template<class... Ts>
struct state:std::variant<Ts...>
{
using std::variant<Ts...>::variant;
template<class T>
constexpr bool is() const
{
return std::holds_alternative<T>(*this);
}
};
const auto print_id (auto& os)
{
return [&os](auto const & val){
if constexpr ( requires { val.id; })
os << val.id;
};
}
template<class... Ts>
class state : public std::variant<Ts...> {
public:
template<class C>
bool is() const { return std::holds_alternative<C>(*this); }
template<class T>
state& operator=(T&& t) {
std::variant<Ts...>::operator=(std::forward<T>(t));
return *this;
}
};
template<typename T> using Id_t = decltype(std::declval<T&>().id);
template<typename T> constexpr bool HasMember_id = std::experimental::is_detected_v< Id_t, T>;
const auto print_id = [] (auto& os) {
return [&os]<typename T>(T const& t) {
if constexpr(HasMember_id<T>)
os << t.id;
};
};
template<class... Ts>
class state : public std::variant<Ts ...> {
public:
using std::variant<Ts ...>::variant;
template<class T>
constexpr auto is() const{
return std::holds_alternative<T>(*this);
}
};
const auto print_id = [] (auto& os) {
return [&](const auto & v){
if constexpr (requires { v.id; })
os << v.id;
};
};