Example
#if __cpp_has_constexpr_function_parameters
void foo(constexpr int x) {
static_assert(42 == x);
}
int main() {
foo(42);
}
#else
template<int x> void foo() {
static_assert(42 == x);
}
int main() {
foo<42>();
}
#endif
Puzzle
- Can you implement a compile-time and run-time map-like accessors for tuple?
template<char...> constexpr auto operator""_id(); // TODO
template<class...> struct tuple; // TODO
int main() {
using namespace boost::ut;
"compile-time tuple map-like access"_test = [] {
constexpr tuple ct{42, [](auto x){ return x; }};
expect(constant<42_i == ct[0_id]>);
expect(constant<43_i == ct[1_id](43)>);
};
"run-time tuple map-like access"_test = [] {
const tuple rt = tuple{1, 2.0};
expect(throws<std::bad_cast>([&rt]{std::any_cast<float>(rt[1]);}));
expect(1_i == std::any_cast<int>(rt[0]));
expect(2.0_d == std::any_cast<double>(rt[1]));
};
}
Solutions
template<char... Cs> constexpr auto operator""_id() {
constexpr std::array chars{Cs...};
constexpr auto index = std::reduce(std::cbegin(chars), std::cend(chars), 0,
[] (const auto i, const auto digit) { return i * 10 + (digit - '0'); });
return std::integral_constant<int, index>{};
}
template<class... Ts> struct tuple : std::tuple<Ts...> {
using std::tuple<Ts...>::tuple;
template <class T, auto N>
constexpr const auto& operator[](std::integral_constant<T, N>) const {
return std::get<N>(*this);
}
constexpr const auto operator[](int i) const {
return [&] <auto... Is> (std::index_sequence<Is...>) {
using F = auto (*)(const tuple<Ts...>&) -> std::any;
constexpr std::array<F, sizeof...(Is)> fs{
[] (const auto& t) -> std::any { return t[std::integral_constant<int, Is>{}]; }...
};
return fs[i](*this);
}(std::index_sequence_for<Ts...>{});
}
};
template <class... Ts>
tuple(Ts...) -> tuple<Ts...>;
template<char... Cs>
constexpr auto operator""_id() {
return []<auto... Ns>(std::index_sequence<Ns...>) {
return std::integral_constant<int, ((int(std::pow(10, sizeof...(Ns) - Ns - 1)) * (Cs - '0')) + ...)>{};
}(std::make_index_sequence<sizeof...(Cs)>{});
}
template<class... Ts>
struct tuple : std::tuple<Ts...> {
using std::tuple<Ts...>::tuple;
template<class TId> constexpr decltype(auto) operator[](const TId id) const {
if constexpr (std::is_integral_v<TId>) {
return std::apply([id](auto... args) { return std::array{std::any{args}...}[id]; }, std::tuple<Ts...>(*this));
} else {
return std::get<TId{}>(*this);
}
}
};
template<class... Ts> tuple(Ts...) -> tuple<Ts...>;
using integer_t = int;
namespace details { // Intentionally using details instead of detail to avoid
// ambiguous namespace
[[nodiscard]] constexpr auto apply_power(const auto power, const auto val) {
return static_cast<integer_t>(std::pow(10, power)) * (val - '0');
}
} // namespace details
template <char... Cs>
[[nodiscard]] constexpr auto operator""_id() {
constexpr auto num_chars = sizeof...(Cs);
return []<auto... Is>(const std::index_sequence<Is...>) {
return std::integral_constant<
integer_t, (details::apply_power(num_chars - Is - 1, Cs) + ...)>{};
}
(std::make_index_sequence<num_chars>{});
};
template <class... Ts>
struct tuple : std::tuple<Ts...> {
using std::tuple<Ts...>::tuple;
template <auto Index>
[[nodiscard]] constexpr auto operator[](
const std::integral_constant<integer_t, Index>) const {
return std::get<Index>(*this);
}
[[nodiscard]] constexpr auto operator[](const auto index) const {
return boost::mp11::mp_with_index<sizeof...(Ts)>(
index, [&](const auto I) { return std::any(std::get<I>(*this)); });
}
};
template <class... Ts>
tuple(Ts...) -> tuple<Ts...>;
template < char ... str >
constexpr auto stoi = []()
{
constexpr int N = sizeof ... (str) ;
return []<auto ... Is> ( std::index_sequence< Is ... > ){
std::size_t m = std::pow( 10, N );//not constexpr but works under gcc
return ( 0 + ... + ( ( str - '0') * (m/=10) ) );
}( std::make_index_sequence<N>{} );
} ();
template< char ... str > constexpr auto operator""_id()
{
return std::index_sequence< stoi<str...> >{} ;
}
template <class T, class ... Ts >
struct tuple:tuple<Ts...>
{
constexpr tuple(T data, Ts... datas):tuple<Ts...>(datas...),data{data}{}
T data;
std::any operator[]( int i ) const
{
if ( i == 0)
return data;
else
return tuple<Ts...>::operator[](i-1);
}
template<std::size_t I>
constexpr auto operator[]( std::index_sequence<I> ) const
{
if constexpr ( I == 0 ){
return data;
} else
{
return tuple<Ts...>::operator[](std::index_sequence<I-1>{});
}
}
};
template <class T>
struct tuple<T>
{
std::any operator[]( int ) const
{
return data;
}
template<std::size_t I>
constexpr auto operator[]( std::index_sequence<I> ) const
{
return data;
}
T data;
};
namespace detail {
template<class T, char C, char... Cs>
consteval T make_integer_constant() {
if constexpr (sizeof...(Cs) == 0) return C - '0';
else return make_integer_constant<T, C>() * 10 + make_integer_constant<T, Cs...>();
}
} // namespace detail
template<char... Cs>
consteval auto operator""_id() {
constexpr auto value = detail::make_integer_constant<std::size_t, Cs...>();
return std::integral_constant<std::remove_cvref_t<decltype(value)>, value>{};
}
template<class... Ts>
struct tuple {
constexpr tuple(Ts&&... args) : t{std::forward<Ts>(args)...} {}
// compile-time
template<std::size_t I>
constexpr auto operator[] (std::integral_constant<std::size_t, I>) const {
return std::get<I>(t);
}
// run-time
constexpr auto operator[] (std::size_t i) const {
return [&] <std::size_t... Is> (std::index_sequence<Is...>) {
constexpr std::array<std::any(*)(const tuple&), sizeof...(Is)> fs{
[] (const auto& t) -> std::any { return t[std::integral_constant<std::size_t, Is>{}]; }...
};
return fs[i](*this);
}(std::index_sequence_for<Ts...>{});
}
private:
std::tuple<Ts...> t;
};
template<int I> struct Id{};
template<char...Cs> constexpr auto operator""_id() {
constexpr std::array<int, sizeof...(Cs)> digits{Cs-'0'...};
constexpr int number = std::accumulate(digits.begin(), digits.end(), 0, [](auto a, auto b) { return 10*a+b;});
return Id<number>{};
}
template<class Tp, size_t N=0>
std::any runtime_get(Tp const& tp, size_t idx) {
if(N==idx)
return std::get<N>(tp);
if constexpr( N+1 < std::tuple_size_v<Tp>)
return runtime_get<Tp, N+1>(tp, idx);
return {};
}
template<class...Args> struct tuple : std::tuple<Args...> {
constexpr tuple(Args...args) : std::tuple<Args...>(args...) {}
template<int I> constexpr auto operator[](Id<I>) const { return std::get<I>(*this); }
auto operator[](int I) const { return runtime_get(static_cast<std::tuple<Args...>>(*this),I); }
};