Skip to content

Instantly share code, notes, and snippets.

@sighingnow
Last active February 26, 2024 12:03
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sighingnow/505d3d5c82237741b4a18147b2f84811 to your computer and use it in GitHub Desktop.
Save sighingnow/505d3d5c82237741b4a18147b2f84811 to your computer and use it in GitHub Desktop.
C++ Tricks.

Tricks in Modern C++

is_specialization_of

Whether the type is a specialization of template T.

template <template <typename...> class T, typename U>
struct is_specialization_of: std::false_type {};

template <template <typename...> class T, typename... Us>
struct is_specialization_of<T, T<Us...>>: std::true_type {};

type-aware index_of

Get the first element which have specific type from a tuple.

template <typename T, std::size_t N, typename... Ts>
struct index_of;

template <typename T, std::size_t N, typename... Ts>
struct index_of<T, N, T, Ts...>: std::integral_constant<int, N> {};

template <typename T, std::size_t N, typename U, typename... Ts>
struct index_of<T, N, U, Ts...>: std::integral_constant<int, index_of<T, N + 1, Ts...>::value> {};

template <typename T, std::size_t N>
struct index_of<T, N>: std::integral_constant<int, -1> {};

passing multiple parameters via a tuple

Pack multiple parameters into a tuple and invoke a function with the tuple.

template <typename F, size_t... Idx, typename... Ts>
constexpr auto call_via_tuple_impl(F f, std::index_sequence<Idx...>, const std::tuple<Ts...> &args) {
    return f(std::get<Idx>(args)...);
}

template <typename F, typename... Ts>
constexpr auto call_via_tuple(F f, const std::tuple<Ts...> &args) {
    return call_via_tuple_impl(f, std::make_index_sequence<sizeof...(Ts)>(), args);
}

size of VA_ARGS

With std::tuple_size:

#define VA_ARGS_SIZE(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value

array to tuple

template<typename Arr, std::size_t... Idx>
constexpr auto array_to_tuple_impl(const Arr &arr, std::index_sequence<Idx...>) {
    return std::make_tuple(arr[Idx]...);
}

template<typename T, std::size_t N>
constexpr auto array_to_tuple(const std::array<T, N> &arr) {
    return array_to_tuple_impl(arr, std::make_index_sequence<N>());
}

make_from_tuple in C++14

namespace detail {
    template <typename C, typename Tup, std::size_t... Idx>
    constexpr decltype(auto) make_from_tuple_cxx14_impl(Tup &&args, std::index_sequence<Idx...>) {
        return C { std::get<Idx>(std::forward<Tup>(args))... };
    }
}

template <typename C, typename Tup>
constexpr decltype(auto) make_from_tuple_cxx14(Tup &&args) {
    return detail::make_from_tuple_cxx14_impl<C>(std::forward<Tup>(args), std::make_index_sequence<std::tuple_size<std::decay_t<Tup>>::value>());
}

is_braces_constructible

namespace detail {
    template <typename, typename, typename = std::void_t<>>
    struct is_braces_constructible_impl: std::false_type {};

    template <typename T, typename... Args>
    struct is_braces_constructible_impl<T, identity<Args...>, std::void_t<decltype(T{ std::declval<Args>()... })>>: std::true_type {};
}

template <typename T, typename... Args>
struct is_braces_constructible: detail::is_braces_constructible_impl<T, identity<Args...>> {};

runtime_visit_tuple

Runtime version of std::get.

template <typename Tup, typename F, std::size_t... Idx>
void runtime_visit_tuple_impl(Tup && tup, std::size_t idx, F && f, std::index_sequence<Idx...>) {
    ((idx == Idx && ((std::forward<F>(f)(std::get<Idx>(tup))), false)), ...);
}
#include <tuple>
#include <utility>
#include <vector>
namespace magic {
/************** common stubs ************************************************/
template <typename...>
struct identity {};
// Make overloaded lambda functions.
// See: https://en.cppreference.com/w/cpp/utility/variant/visit
template <typename... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
};
template <typename... Ts>
overloaded(Ts...)->overloaded<Ts...>;
/************** is_specialization_of *****************************************/
// whether the type is a specialization of template T.
// e.g.,
//
// is_specialization_of<std::tuple, std::tuple<int>>::value ===> true
// is_specialization_of<std::tuple, std::vector<int>>::value ===> false
template <template <typename...> class T, typename U>
struct is_specialization_of : std::false_type {};
template <template <typename...> class T, typename... Us>
struct is_specialization_of<T, T<Us...>> : std::true_type {};
// example: is_tuple
template <typename T>
struct is_tuple : is_specialization_of<std::tuple, typename std::decay<T>::type> {};
/************** get_first ****************************************************/
// Get the first element with specific type from tuple.
// e.g.,
//
// get_first<int>(std::make_tuple('a', 1.0, 2, 3)) ===> 2
// get_first<int>(std::make_tuple('a', 1.0, 2.0, 3.0)) ===> compile error
namespace detail {
template <typename T, std::size_t N, typename... Ts>
struct index_of;
template <typename T, std::size_t N, typename... Ts>
struct index_of<T, N, T, Ts...> : std::integral_constant<int, N> {};
template <typename T, std::size_t N, typename U, typename... Ts>
struct index_of<T, N, U, Ts...>
: std::integral_constant<int, index_of<T, N + 1, Ts...>::value> {};
template <typename T, std::size_t N>
struct index_of<T, N> : std::integral_constant<int, -1> {};
} // namespace detail
namespace detail::cxx14 {
template <typename T, std::size_t N, typename... Ts>
struct index_of;
template <typename T, std::size_t N, typename... Ts>
struct index_of<T, N, T, Ts...> {
static constexpr auto value = N;
};
template <typename T, std::size_t N, typename U, typename... Ts>
struct index_of<T, N, U, Ts...> {
static constexpr auto value = index_of<T, N + 1, Ts...>::value;
};
template <typename T, std::size_t N>
struct index_of<T, N> {
static constexpr auto value = -1;
};
} // namespace detail::cxx14
template <typename T, typename... Ts>
constexpr T get_first(const std::tuple<Ts...> &tup) {
return std::move(std::get<detail::index_of<T, 0, Ts...>::value>(tup));
}
/************** call_via_tuple ***********************************************/
// Passing parameters inside a tuple to invoke a function.
//
// With C++14/17, we can use `auto` as return type.
// With C++11, we need to use `decltype(...)` as return type.
namespace detail {
template <typename F, size_t... Idx, typename... Ts>
constexpr auto call_via_tuple_impl(
F f, std::index_sequence<Idx...>, const std::tuple<Ts...> &args) {
return f(std::get<Idx>(args)...);
}
} // namespace detail
template <typename F, typename... Ts>
constexpr auto call_via_tuple(F f, const std::tuple<Ts...> &args) {
return detail::call_via_tuple_impl(f, std::make_index_sequence<sizeof...(Ts)>(), args);
}
/************** VA_ARGS_SIZE *************************************************/
#define VA_ARGS_SIZE(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value
/************** array_to_tuple ***********************************************/
namespace detail {
template <typename Arr, std::size_t... Idx>
constexpr auto array_to_tuple_impl(const Arr &arr, std::index_sequence<Idx...>) {
return std::make_tuple(arr[Idx]...);
}
} // namespace detail
template <typename T, std::size_t N>
constexpr auto array_to_tuple(const std::array<T, N> &arr) {
return detail::array_to_tuple_impl(arr, std::make_index_sequence<N>());
}
/************** make_from_tuple in C++14 *************************************/
namespace detail {
template <typename C, typename Tup, std::size_t... Idx>
constexpr decltype(auto) make_from_tuple_cxx14_impl(Tup &&args, std::index_sequence<Idx...>) {
return C{std::get<Idx>(std::forward<Tup>(args))...};
}
} // namespace detail
template <typename C, typename Tup>
constexpr decltype(auto) make_from_tuple_cxx14(Tup &&args) {
return detail::make_from_tuple_cxx14_impl<C>(std::forward<Tup>(args),
std::make_index_sequence<std::tuple_size<std::decay_t<Tup>>::value>());
}
/************** is_braces_constructible **************************************/
namespace detail {
template <typename, typename, typename = std::void_t<>>
struct is_braces_constructible_impl : std::false_type {};
template <typename T, typename... Args>
struct is_braces_constructible_impl<T, identity<Args...>,
std::void_t<decltype(T{std::declval<Args>()...})>> : std::true_type {};
} // namespace detail
template <typename T, typename... Args>
struct is_braces_constructible : detail::is_braces_constructible_impl<T, identity<Args...>> {};
/************** runtime_visit_tuple ******************************************/
// Runtime version of std::get, the index may not be constant.
namespace detail {
template <typename Tup, typename F, std::size_t... Idx>
void runtime_visit_tuple_impl(Tup &&tup, std::size_t idx, F &&f, std::index_sequence<Idx...>) {
((idx == Idx && ((std::forward<F>(f)(std::get<Idx>(tup))), false)), ...);
}
} // namespace detail
template <typename Tup, typename F>
void runtime_visit_tuple(Tup &&tup, std::size_t idx, F &&f) {
detail::runtime_visit_tuple_impl(std::move(tup), idx, std::forward<F>(f),
std::make_index_sequence<std::tuple_size<std::decay_t<Tup>>::value>());
}
} // namespace magic
#include <cassert>
#include "magic.hpp"
void test_is_specialization_of() {
static_assert(magic::is_specialization_of<std::tuple, std::tuple<int, int>>::value);
static_assert(!magic::is_specialization_of<std::tuple, int>::value);
static_assert(!magic::is_specialization_of<std::tuple, std::vector<int>>::value);
static_assert(magic::is_tuple<const std::tuple<int, int>>::value);
static_assert(magic::is_tuple<const std::tuple<const int, int>>::value);
}
void test_get_first() {
constexpr auto tup = std::make_tuple('x', 10, 'y', 1.0f);
static_assert(magic::get_first<int>(tup) == 10);
static_assert(magic::get_first<char>(tup) == 'x');
static_assert(magic::get_first<char>(tup) != 'y');
}
void test_call_via_tuple() {
constexpr auto tup = std::make_tuple(1, 2, 3);
auto f = [](int a, int b, int c) -> auto { return a + b + c; };
static_assert(magic::call_via_tuple(f, tup) == 6);
}
void test_array_to_tuple() {
constexpr std::array<int, 3> arr = {1, 2, 3};
constexpr auto tup = magic::array_to_tuple(arr);
static_assert(magic::is_tuple<decltype(tup)>::value);
static_assert(std::get<0>(tup) == 1);
static_assert(std::get<1>(tup) == 2);
static_assert(std::get<2>(tup) == 3);
}
void test_make_from_tuple_cxx14() {
struct sample_pod_t {
int x;
double y;
char z;
};
constexpr sample_pod_t v
= magic::make_from_tuple_cxx14<sample_pod_t>(std::make_tuple(1, 1.0, 'z'));
static_assert(v.x == 1);
static_assert(v.y == 1.0);
static_assert(v.z == 'z');
}
void test_is_braces_constructible() {
static_assert(magic::is_braces_constructible<std::tuple<int, int>, int, int>::value);
static_assert(!magic::is_braces_constructible<std::tuple<int, int, int>, int, int>::value);
}
void test_runtime_visit_tuple() {
std::tuple<int, char, float> xs = {1, 'x', 2.0};
for (std::size_t i = 0; i < 3; ++i) {
magic::runtime_visit_tuple(xs, i,
magic::overloaded{
[](int v) { assert(v == 1); },
[](char v) { assert(v == 'x'); },
[](double v) { assert(v == 2.0); },
});
}
}
int main() {
test_is_specialization_of();
test_get_first();
test_call_via_tuple();
test_array_to_tuple();
test_make_from_tuple_cxx14();
test_is_braces_constructible();
test_runtime_visit_tuple();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment