diff --git a/cpp/src/arrow/util/functional.h b/cpp/src/arrow/util/functional.h index 3588e8540e8..1795ca68017 100644 --- a/cpp/src/arrow/util/functional.h +++ b/cpp/src/arrow/util/functional.h @@ -126,5 +126,17 @@ class FnOnce { std::unique_ptr impl_; }; +/// A callable object that simply forwards to T's constructor. +/// +/// This allows passing `Constructor()` as a callable argument for applying +/// the T constructor without hand-writing a lambda. +template +struct Constructor { + template + T operator()(Values&&... values) { + return T(std::forward(values)...); + } +}; + } // namespace internal } // namespace arrow diff --git a/cpp/src/arrow/util/stl_util_test.cc b/cpp/src/arrow/util/stl_util_test.cc index 4746c6f3700..c2031540484 100644 --- a/cpp/src/arrow/util/stl_util_test.cc +++ b/cpp/src/arrow/util/stl_util_test.cc @@ -21,6 +21,9 @@ #include #include +#include "arrow/result.h" +#include "arrow/testing/gtest_util.h" +#include "arrow/util/functional.h" #include "arrow/util/sort.h" #include "arrow/util/string.h" #include "arrow/util/vector.h" @@ -92,5 +95,22 @@ TEST(StlUtilTest, ArgSortPermute) { ExpectSortPermutation({b, c, d, e, a, f}, {4, 0, 1, 2, 3, 5}, 2); } +TEST(StlUtilTest, MapEmplaceBack) { + auto all_good = EmplacedMappedVector>( + Constructor(), 1, 2, 3); + + ASSERT_EQ(all_good[0].ValueUnsafe(), 1); + ASSERT_EQ(all_good[1].ValueUnsafe(), 2); + ASSERT_EQ(all_good[2].ValueUnsafe(), 3); + ASSERT_EQ(all_good.size(), 3); + + auto some_bad = EmplacedVector>( + MoveOnlyDataType(1), Status::Invalid("XYZ"), Status::IOError("XYZ")); + + ASSERT_EQ(some_bad[0].ValueUnsafe(), 1); + ASSERT_TRUE(some_bad[1].status().IsInvalid()); + ASSERT_TRUE(some_bad[2].status().IsIOError()); +} + } // namespace internal } // namespace arrow diff --git a/cpp/src/arrow/util/vector.h b/cpp/src/arrow/util/vector.h index cbd874dacae..5e0207a6ed6 100644 --- a/cpp/src/arrow/util/vector.h +++ b/cpp/src/arrow/util/vector.h @@ -81,5 +81,43 @@ std::vector FilterVector(std::vector values, Predicate&& predicate) { return values; } +template +void MapEmplaceBack(std::vector* out, Fn&& fn) {} + +template +void MapEmplaceBack(std::vector* out, Fn&& fn, Head&& value, Values&&... tail_values) { + out->emplace_back(fn(std::forward(value))); + MapEmplaceBack(out, std::forward(fn), std::forward(tail_values)...); +} + +template +void EmplaceBack(std::vector* out) {} + +template +void EmplaceBack(std::vector* out, Head&& value, Values&&... tail_values) { + out->emplace_back(std::forward(value)); + EmplaceBack(out, std::forward(tail_values)...); +} + +// Construct a vector by emplacing with each of the provided values. +// Note this is less flexible than manual calls to emplace_back(), since +// only 1-argument constructors can be invoked. +template +std::vector EmplacedVector(Values&&... values) { + std::vector result; + result.reserve(sizeof...(values)); + EmplaceBack(&result, std::forward(values)...); + return result; +} + +// Like EmplacedVector, but emplace the result of calling `Fn` on the `values`. +template +std::vector EmplacedMappedVector(Fn&& fn, Values&&... values) { + std::vector result; + result.reserve(sizeof...(values)); + MapEmplaceBack(&result, std::forward(fn), std::forward(values)...); + return result; +} + } // namespace internal } // namespace arrow