Last active
May 29, 2023 15:24
-
-
Save obidavis/a65e8af309299fc5ebe13f0848d2f398 to your computer and use it in GitHub Desktop.
Construct a variant alternative from a runtime index. Uses a static constexpr table to promote runtime integers to std::integral_constants, and forwards these along with args to the variant alternative's constructor. Useful for deserialisation? TODO: bounds checks, how to handle invalid indices...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <utility> | |
#include <variant> | |
/** | |
* @brief Gives the template `B` specialised with | |
* the parameter pack of the specialised template `A`. | |
* e.g. if `A` is a `std::tuple<int, float>` and `B` is | |
* a `std::variant`, `rebind_t<A, B>` will be a | |
* `std::variant<int, float>`. | |
* | |
* @tparam A The specialised template | |
* @tparam B The template to specialise with the parameters | |
* of A | |
*/ | |
template<class A, template<class...> class B> | |
struct rebind; | |
template<template<class...> class A, class... T, template<class...> class B> | |
struct rebind<A<T...>, B> { | |
using type = B<T...>; | |
}; | |
template<class A, template<class...> class B> | |
using rebind_t = typename rebind<A, B>::type; | |
// Same as above, but for non-type template parameters of type std::size_t | |
template<class A, template<std::size_t...> class B> | |
struct rebind_indices; | |
template<template<typename, std::size_t...> class A, typename Int, std::size_t... Is, template<std::size_t...> class B> | |
struct rebind_indices<A<Int, Is...>, B> { | |
using type = B<Is...>; | |
}; | |
template<class A, template<std::size_t...> class B> | |
using rebind_indices_t = typename rebind_indices<A, B>::type; | |
template <std::size_t ...Is> | |
using index_variant_impl = std::variant<std::integral_constant<std::size_t, Is>...>; | |
template <std::size_t N> | |
using index_variant = rebind_indices_t<std::make_index_sequence<N>, index_variant_impl>; | |
/** | |
* @brief A wrapper around a static table of variants of types Ts. | |
* Each element in the table is a default constructed instance of the | |
* variant alternative at that element's index. | |
* | |
* @tparam Ts The types to instantiate the variants with | |
*/ | |
template <typename ...Ts> | |
struct default_variant_table | |
{ | |
constexpr static std::variant<Ts...> table[] = { Ts{}... }; | |
constexpr static auto get(std::size_t index) { return table[index]; } | |
}; | |
// https://stackoverflow.com/questions/16337610/how-to-know-if-a-type-is-a-specialization-of-stdvector | |
template<typename Test, template<typename...> class Ref> | |
struct is_specialization : std::false_type {}; | |
template<template<typename...> class Ref, typename... Args> | |
struct is_specialization<Ref<Args...>, Ref>: std::true_type {}; | |
/** | |
* @brief Constructs a variant with the alternative specified | |
* by the *runtime* index i and initializes the contained value | |
* with the arguments std::forward<Args>(args).... | |
* (cf. https://en.cppreference.com/w/cpp/utility/variant/variant (7)) | |
* | |
* @tparam Variant The variant the alternative of which to construct | |
* @tparam Args Types of args | |
* @param index The variant alternative to construct | |
* @param args Arguments to pass to the constructor of | |
* the variant alternative | |
* @return Variant alternative constructed by args | |
*/ | |
template <typename Variant, typename ...Args> | |
Variant construct_variant_alternative(std::size_t i, Args &&... args) | |
{ | |
static_assert(is_specialization<Variant, std::variant>::value, "Must provide a variant"); | |
using indices_table = rebind_t< | |
index_variant<std::variant_size_v<Variant>>, | |
default_variant_table>; | |
return std::visit( | |
[&args...](auto &&index) -> Variant | |
{ | |
using index_constant = std::decay_t<decltype(index)>; | |
return Variant(std::in_place_index<index_constant::value>, std::forward<Args>(args)... ); | |
}, | |
indices_table::get(i) | |
); | |
} | |
// // Example usage | |
// #include <iostream> | |
// int main() | |
// { | |
// using v = std::variant<short, long>; | |
// std::size_t index; | |
// std::cout << "Enter index: "; | |
// std::cin >> index; | |
// index %= std::variant_size<v>::value; | |
// auto x = construct_variant_alternative<v>(index, 1'000'000); | |
// // prints 1000000 or 16960 | |
// std::visit([](auto arg) | |
// { | |
// std::cout << "Value has width of " << sizeof(arg) << " bytes "; | |
// std::cout << "and value of " << arg << "\n"; | |
// }, x); | |
// return 0; | |
// } | |
// Assembly comparision with switch alternative: | |
// https://godbolt.org/z/fs94jME9T |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment