Skip to content

Instantly share code, notes, and snippets.

@furfurylic
Last active February 12, 2023 09:14
Show Gist options
  • Save furfurylic/0a91c15f4f9f9d6ec70cdc398d34f021 to your computer and use it in GitHub Desktop.
Save furfurylic/0a91c15f4f9f9d6ec70cdc398d34f021 to your computer and use it in GitHub Desktop.
lab - tuple_get_n
Create a new tuple out of an existing tuple in C++
cmake_minimum_required(VERSION 3.13)
project(test_tuple_get_n CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
enable_testing()
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.13.0
)
FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
FetchContent_Populate(googletest)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
add_subdirectory(${googletest_SOURCE_DIR}
${googletest_BINARY_DIR}
EXCLUDE_FROM_ALL)
endif()
set_target_properties(gtest gtest_main PROPERTIES FOLDER "Dependencies")
add_executable(test_tuple_get_n)
target_sources(test_tuple_get_n PRIVATE
tuple_get_n.hpp
tuple_tail.hpp
test_tuple_get_n.cpp
test_tuple_tail.cpp
)
target_compile_features(test_tuple_get_n PRIVATE cxx_std_17)
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(test_tuple_get_n PRIVATE
/MP /W4 /bigobj
$<$<CONFIG:MinSizeRel>:/wd4702>
$<$<CONFIG:Release>:/wd4702>
$<$<CONFIG:RelWithDebInfo>:/wd4702>
)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_definitions(test_tuple_get_n PRIVATE
$<$<NOT:$<CONFIG:Debug>>:NDEBUG>
)
target_compile_options(test_tuple_get_n PRIVATE
-Wall -Wextra -pedantic-errors -Werror=pedantic
$<$<CONFIG:Debug>:-O0 -g3>
$<$<CONFIG:RelWithDebInfo>:-O2 -g3>
)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_definitions(test_tuple_get_n PRIVATE
$<$<NOT:$<CONFIG:Debug>>:NDEBUG>
)
target_compile_options(test_tuple_get_n PRIVATE
-Wall -Wextra -pedantic-errors -Werror=pedantic
-Wno-gnu-zero-variadic-macro-arguments
$<$<CONFIG:Debug>:-O0 -g3>
$<$<CONFIG:RelWithDebInfo>:-O2 -g3>
)
endif()
target_link_libraries(test_tuple_get_n PRIVATE gtest gtest_main)
add_test(
NAME test_tuple_get_n
COMMAND $<TARGET_FILE:test_tuple_get_n>
)
/*
* These codes are available under CC0 1.0 Universal.
* https://creativecommons.org/publicdomain/zero/1.0/deed
*/
#include <string>
#include <tuple>
#include <type_traits>
#include <gtest/gtest.h>
#include "tuple_get_n.hpp"
using namespace std::literals::string_literals;
using namespace furfurylic::lab;
namespace {
// A type whose moved-from states are value-initialized ones
template <class T>
struct move_to_valinit {
T t;
template <class U,
std::enable_if_t<!std::is_base_of_v<move_to_valinit, std::decay_t<U>>>*
= nullptr>
explicit move_to_valinit(U&& u) : t(std::forward<U>(u))
{}
move_to_valinit(move_to_valinit&& other)
noexcept(std::is_nothrow_move_assignable_v<T>
&& std::is_nothrow_default_constructible_v<T>) :// to squelch VS2019
t(std::exchange(other.t, T()))
{}
move_to_valinit(const move_to_valinit&) = default;
};
}
TEST(TestTupleGetN, Copy) {
using S = move_to_valinit<std::string>;
std::tuple<int, S, double, long> t(10, S("ABC"), 3.14, -20);
auto u = get_n<3, 1>(t);
static_assert(std::is_same_v<std::tuple<long, move_to_valinit<std::string>>,
decltype(u)>);
ASSERT_EQ(-20, std::get<0>(u));
ASSERT_EQ("ABC"s, std::get<1>(u).t);
ASSERT_EQ("ABC"s, std::get<1>(t).t); // of course, not moved from
}
TEST(TestTupleGetN, Move) {
using S = move_to_valinit<std::string>;
std::tuple<int, S, double, long> t(10, S("ABC"), 3.14, -20);
auto u = get_n<3, 1>(std::move(t));
static_assert(std::is_same_v<std::tuple<long, S>, decltype(u)>);
ASSERT_EQ(-20, std::get<0>(u));
ASSERT_EQ("ABC"s, std::get<1>(u).t);
ASSERT_TRUE(std::get<1>(t).t.empty()); // moved-from state
}
TEST(TestTupleGetN, LvalueRef) {
int l = 1;
float m = 5.0;
char n = 'A';
auto t = std::tie(l, m, n);
auto u = get_n<2, 0>(t);
static_assert(std::is_same_v<std::tuple<char&, int&>, decltype(u)>);
std::get<1>(u) *= -1; // modify n through an lvalue reference in u
ASSERT_EQ(-1, l);
auto v = get_n<1>(std::move(u));
static_assert(std::is_same_v<std::tuple<int&>, decltype(v)>);
// holds an lvalue reference (not rvalue one)
ASSERT_EQ(&l, &std::get<0>(v));
}
TEST(TestTupleGetN, RvalueRef) {
using S = move_to_valinit<std::string>;
int l = 1;
S m("XYZ");
auto t = std::forward_as_tuple(std::move(l), std::move(m));
static_assert(std::is_same_v<std::tuple<int&&, S&&>, decltype(t)>);
// auto u = get_n<0, 1>(t);
// This does not compile, which corresponds with the below code, which also
// does not
// auto u = t;
auto v = get_n<0, 1>(std::move(t));
static_assert(std::is_same_v<decltype(t), decltype(v)>);
ASSERT_EQ("XYZ"s, m.t); // rvalue references copied but no move occurred
std::string s = std::get<1>(std::move(v)).t; // move occurs
ASSERT_EQ("XYZ"s, s);
ASSERT_TRUE(m.t.empty());
}
static_assert(std::make_tuple(42, 800)
== get_n<2, 0>(std::make_tuple(800, 3.14, 42)));
/*
* These codes are available under CC0 1.0 Universal.
* https://creativecommons.org/publicdomain/zero/1.0/deed
*/
#include <string>
#include <tuple>
#include <type_traits>
#include <gtest/gtest.h>
#include "tuple_tail.hpp"
using namespace furfurylic::lab;
TEST(TestTail, All) {
int n = 42;
std::tuple<std::string, int&, double> v("ABC", n, 3.14);
auto t = tail(v);
static_assert(std::is_same_v<std::tuple<int&, double>, decltype(t)>);
std::get<int&>(t) = 100;
ASSERT_EQ(100, n);
const auto t1 = std::get<double>(t);
ASSERT_EQ(3.14, t1);
}
static_assert(std::make_tuple(2, 3) == tail(std::make_tuple(1, 2, 3)));
static_assert(std::is_same_v<std::tuple<>,
decltype(tail(std::make_tuple(42)))>);
/*
* These codes are available under CC0 1.0 Universal.
* https://creativecommons.org/publicdomain/zero/1.0/deed
*/
#ifndef FURFURYLIC_LAB_GUARD_3E8575E2_2433_45EE_9B72_CA8C38DFA233
#define FURFURYLIC_LAB_GUARD_3E8575E2_2433_45EE_9B72_CA8C38DFA233
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
namespace furfurylic::lab {
template <std::size_t... Is, class Tuple>
constexpr std::tuple<std::tuple_element_t<Is, std::decay_t<Tuple>>...>
get_n(Tuple&& t) {
return { std::get<Is>(std::forward<Tuple>(t))... };
}
}
#endif
/*
* These codes are available under CC0 1.0 Universal.
* https://creativecommons.org/publicdomain/zero/1.0/deed
*/
#ifndef FURFURYLIC_LAB_GUARD_62190A1A_B9A3_4AB8_BDC1_C013D5B90AC5
#define FURFURYLIC_LAB_GUARD_62190A1A_B9A3_4AB8_BDC1_C013D5B90AC5
#include <functional>
#include "tuple_get_n.hpp"
namespace furfurylic::lab {
namespace detail {
template <std::size_t... Is, class Tuple>
constexpr auto tail_impl(std::index_sequence<Is...>, Tuple&& t) {
return get_n<(Is + 1)...>(std::forward<Tuple>(t));
}
template <class T0, class... Ts>
std::tuple<Ts...> tail_type(const std::tuple<T0, Ts...>&);
}
template <class Tuple>
constexpr decltype(detail::tail_type(std::declval<Tuple>())) tail(Tuple&& t) {
return detail::tail_impl(
std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>> - 1>(),
std::forward<Tuple>(t));
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment