Skip to content

Instantly share code, notes, and snippets.

@ldionne
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ldionne/fd460b13ef26856b1f3b to your computer and use it in GitHub Desktop.
Save ldionne/fd460b13ef26856b1f3b to your computer and use it in GitHub Desktop.
/*!
* Barebones zip_with and fmap for std::tuple and std::integer_sequence.
*
* Copyright Louis Dionne 2014
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE.md or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
// Compiled with:
// clang++-3.5 -Wall -Wextra -pedantic -std=c++1y -c zip_with.cpp
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
template <typename F, typename ...T, std::size_t ...Index>
constexpr auto fmap_impl(F f, std::tuple<T...> xs, std::index_sequence<Index...>) {
return std::make_tuple(f(std::get<Index>(xs))...);
}
template <typename F, typename ...T>
constexpr auto fmap(F f, std::tuple<T...> xs) {
return fmap_impl(f, xs, std::index_sequence_for<T...>{});
}
// Note that we can't return a std::integer_sequence<T, f(Indices)...>
// because we can't go from runtime arguments to template arguments, even
// in constexpr.
template <typename F, typename T, T ...Indices>
constexpr auto fmap(F f, std::integer_sequence<T, Indices...>) {
return std::make_tuple(f(std::integral_constant<T, Indices>{})...);
}
// Requires a non-empty array.
template <typename T, std::size_t N>
constexpr T min(T (&arr)[N]) {
auto low = arr[0];
for (auto n: arr)
if (n < low)
low = n;
return low;
}
template <typename F, typename ...Tuple>
constexpr auto zip_with(F f, Tuple ...xs) {
constexpr std::size_t lengths[] = {std::tuple_size<Tuple>{}...};
constexpr std::size_t zip_length = min(lengths);
return fmap(
// We need constexpr lambdas badly...
[=](auto index) { return f(std::get<decltype(index){}>(xs)...); },
std::make_index_sequence<zip_length>{}
);
}
///////////////////////////////////////////////////////////////////////
// Tests
///////////////////////////////////////////////////////////////////////
#include <cassert>
#include <functional>
// fmap on tuple
static_assert(fmap(std::negate<>{}, std::make_tuple()) == std::make_tuple(), "");
static_assert(fmap(std::negate<>{}, std::make_tuple(1)) == std::make_tuple(-1), "");
static_assert(fmap(std::negate<>{}, std::make_tuple(1, 2)) == std::make_tuple(-1, -2), "");
static_assert(fmap(std::negate<>{}, std::make_tuple(1, 2, 3)) == std::make_tuple(-1, -2, -3), "");
// fmap on integer_sequence
static_assert(fmap(std::negate<>{}, std::make_index_sequence<0>{}) == std::make_tuple(), "");
static_assert(fmap(std::negate<>{}, std::make_index_sequence<1>{}) == std::make_tuple(0), "");
static_assert(fmap(std::negate<>{}, std::make_index_sequence<2>{}) == std::make_tuple(0, -1), "");
static_assert(fmap(std::negate<>{}, std::make_index_sequence<3>{}) == std::make_tuple(0, -1, -2), "");
static_assert(fmap(std::negate<>{}, std::make_index_sequence<4>{}) == std::make_tuple(0, -1, -2, -3), "");
int main() {
// zip_with on tuples (not constexpr)
assert(
zip_with(std::plus<>{}, std::make_tuple(), std::make_tuple())
==
std::make_tuple()
);
assert(
zip_with(std::plus<>{}, std::make_tuple(1), std::make_tuple())
==
std::make_tuple()
);
assert(
zip_with(std::plus<>{}, std::make_tuple(), std::make_tuple(3))
==
std::make_tuple()
);
assert(
zip_with(std::plus<>{}, std::make_tuple(1), std::make_tuple(3))
==
std::make_tuple(1 + 3)
);
assert(
zip_with(std::plus<>{}, std::make_tuple(1, 2), std::make_tuple(3, 4))
==
std::make_tuple(1 + 3, 2 + 4)
);
assert(
zip_with(std::plus<>{}, std::make_tuple(1, 2, 3, 4), std::make_tuple(5, 6, 7))
==
std::make_tuple(1 + 5, 2 + 6, 3 + 7)
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment