Skip to content

Instantly share code, notes, and snippets.

@furfurylic
Last active February 12, 2023 09:13
Show Gist options
  • Save furfurylic/c882203a4d233b86d364543495c240cb to your computer and use it in GitHub Desktop.
Save furfurylic/c882203a4d233b86d364543495c240cb to your computer and use it in GitHub Desktop.
lab - tuple_transform
Apply a function to each corresponding element of tuples and make a new tuple of the return values
cmake_minimum_required(VERSION 3.13)
project(test_tuple_transform 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_transform)
target_sources(test_tuple_transform PRIVATE
tuple_transform.hpp
test_tuple_transform.cpp
)
target_compile_features(test_tuple_transform PRIVATE cxx_std_17)
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(test_tuple_transform 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_transform PRIVATE
$<$<NOT:$<CONFIG:Debug>>:NDEBUG>
)
target_compile_options(test_tuple_transform 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_transform PRIVATE
$<$<NOT:$<CONFIG:Debug>>:NDEBUG>
)
target_compile_options(test_tuple_transform 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_transform PRIVATE gtest gtest_main)
add_test(
NAME test_tuple_transform
COMMAND $<TARGET_FILE:test_tuple_transform>
)
/*
* These codes are available under CC0 1.0 Universal.
* https://creativecommons.org/publicdomain/zero/1.0/deed
*/
#include <functional>
#include <memory>
#include <string>
#include <tuple>
#include <type_traits>
#include <vector>
#include <gtest/gtest.h>
#include "tuple_transform.hpp"
using namespace std::literals::string_literals;
using namespace furfurylic::lab;
TEST(TestTupleTransform, Basics) {
auto t1 = std::make_tuple("break"s, 2);
auto t2 = std::make_tuple("fast"s, 3);
auto u = transform(std::plus<>(), t1, t2);
ASSERT_EQ(std::make_tuple("breakfast"s, 5), u);
}
TEST(TestTupleTransform, Move) {
std::tuple<std::unique_ptr<int>> t(new int(100));
const auto pt = std::get<0>(t).get();
auto u = transform([](std::unique_ptr<int>&& e1) {
std::vector<std::unique_ptr<int>> v;
v.push_back(std::move(e1));
return v;
}, std::move(t));
static_assert(std::is_same_v<std::tuple<std::vector<std::unique_ptr<int>>>,
decltype(u)>);
ASSERT_TRUE(!std::get<0>(t)); // moved-from state
ASSERT_EQ(pt, std::get<0>(u)[0].get()); // takes over the pointee of t
}
static_assert(std::make_tuple(6, 54.0)
== transform(std::multiplies<>(),
std::make_tuple(2, 9.0), std::make_tuple(3, 6.0)));
static_assert(std::is_same_v<
std::tuple<>,
decltype(transform(std::multiplies<>(), std::tuple<>()))>);
/*
* These codes are available under CC0 1.0 Universal.
* https://creativecommons.org/publicdomain/zero/1.0/deed
*/
#ifndef FURFURYLIC_LAB_GUARD_F0826CB7_4DF1_403B_8C6B_1CE6CA2D8619
#define FURFURYLIC_LAB_GUARD_F0826CB7_4DF1_403B_8C6B_1CE6CA2D8619
#include <algorithm>
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
namespace furfurylic::lab {
namespace detail {
template <std::size_t I, class F, class... Tuples>
constexpr auto apply(F f, Tuples&&... t) {
return f(std::get<I>(std::forward<Tuples>(t))...);
}
template <std::size_t... Is, class F, class... Tuples>
constexpr auto transform_impl(std::index_sequence<Is...>,
[[maybe_unused]] F f, [[maybe_unused]] Tuples&&... ts) {
return std::make_tuple(apply<Is>(f, std::forward<Tuples>(ts)...)...);
}
}
template <class F, class... Tuples>
constexpr auto transform(F f, Tuples&&... ts) {
constexpr auto Min = std::min({std::tuple_size_v<std::decay_t<Tuples>>...});
constexpr auto Max = std::max({std::tuple_size_v<std::decay_t<Tuples>>...});
static_assert(Min == Max, "Inconsistent tuple sizes");
return detail::transform_impl(std::make_index_sequence<Min>(),
std::move(f), std::forward<Tuples>(ts)...);
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment