Skip to content
1 change: 1 addition & 0 deletions benchmarks/src/vector_bool_transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ void common_args(auto bm) {

BENCHMARK(transform_two_inputs_aligned<logical_and<>>)->Apply(common_args);
BENCHMARK(transform_two_inputs_aligned<logical_or<>>)->Apply(common_args);
BENCHMARK(transform_two_inputs_aligned<less<>>)->Apply(common_args);
BENCHMARK(transform_one_input_aligned<logical_not<>>)->Apply(common_args);

BENCHMARK_MAIN();
7 changes: 4 additions & 3 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -4228,7 +4228,8 @@ _CONSTEXPR20 _OutIt transform(const _InIt _First, const _InIt _Last, _OutIt _Des
const auto _ULast = _STD _Get_unwrapped(_Last);
auto _UDest = _STD _Get_unwrapped_n(_Dest, _STD _Idl_distance<_InIt>(_UFirst, _ULast));

if constexpr (_Is_vb_iterator<_InIt> && _Is_vb_iterator<_OutIt, true> && !is_same_v<_Map_vb_functor_t<_Fn>, void>) {
if constexpr (_Is_vb_iterator<decltype(_UFirst)> && _Is_vb_iterator<decltype(_UDest), true>
&& !is_void_v<_Map_vb_functor_t<_Fn>>) {
if (_UFirst._Myoff == 0 && _UDest._Myoff == 0) {
_UDest = _Transform_vbool_aligned(_UFirst, _ULast, _UDest, _Map_vb_functor_t<_Fn>{});
_STD _Seek_wrapped(_Dest, _UDest);
Expand Down Expand Up @@ -4260,8 +4261,8 @@ _CONSTEXPR20 _OutIt transform(
auto _UFirst2 = _STD _Get_unwrapped_n(_First2, _Count);
auto _UDest = _STD _Get_unwrapped_n(_Dest, _Count);

if constexpr (_Is_vb_iterator<_InIt1> && _Is_vb_iterator<_InIt2> && _Is_vb_iterator<_OutIt, true>
&& !is_same_v<_Map_vb_functor_t<_Fn>, void>) {
if constexpr (_Is_vb_iterator<decltype(_UFirst1)> && _Is_vb_iterator<decltype(_UFirst2)>
&& _Is_vb_iterator<decltype(_UDest), true> && !is_void_v<_Map_vb_functor_t<_Fn>>) {
if (_UFirst1._Myoff == 0 && _UFirst2._Myoff == 0 && _UDest._Myoff == 0) {
_UDest = _Transform_vbool_aligned(_UFirst1, _ULast1, _UFirst2, _UDest, _Map_vb_functor_t<_Fn>{});
_STD _Seek_wrapped(_Dest, _UDest);
Expand Down
62 changes: 61 additions & 1 deletion stl/inc/functional
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,48 @@ struct _Bit_xnor {
using is_transparent = int;
};

struct _Bit_imply {
template <class _Ty1, class _Ty2>
_NODISCARD constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const //
-> decltype(~_STD forward<_Ty1>(_Left) | _STD forward<_Ty2>(_Right)) {
return ~_STD forward<_Ty1>(_Left) | _STD forward<_Ty2>(_Right);
}

using is_transparent = int;
};

struct _Bit_cimply {
template <class _Ty1, class _Ty2>
_NODISCARD constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const //
-> decltype(_STD forward<_Ty1>(_Left) | ~_STD forward<_Ty2>(_Right)) {
return _STD forward<_Ty1>(_Left) | ~_STD forward<_Ty2>(_Right);
}

using is_transparent = int;
};

struct _Bit_nimply {
template <class _Ty1, class _Ty2>
_NODISCARD constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const //
-> decltype(_STD forward<_Ty1>(_Left) & ~_STD forward<_Ty2>(_Right)) {
return _STD forward<_Ty1>(_Left) & ~_STD forward<_Ty2>(_Right);
}

using is_transparent = int;
};

struct _Bit_cnimply {
template <class _Ty1, class _Ty2>
_NODISCARD constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const //
-> decltype(~_STD forward<_Ty1>(_Left) & _STD forward<_Ty2>(_Right)) {
return ~_STD forward<_Ty1>(_Left) & _STD forward<_Ty2>(_Right);
}

using is_transparent = int;
};

template <class _Ty>
constexpr bool _Is_vbool_functor_arg = is_same_v<_Ty, void> || is_integral_v<_Ty>;
constexpr bool _Is_vbool_functor_arg = is_void_v<_Ty> || is_integral_v<_Ty>;

template <class _Ty>
struct _Map_vb_functor<logical_and<_Ty>> {
Expand Down Expand Up @@ -308,6 +348,26 @@ struct _Map_vb_functor<equal_to<_Ty>> {
using type = conditional_t<_Is_vbool_functor_arg<_Ty>, _Bit_xnor, void>;
};

template <class _Ty>
struct _Map_vb_functor<less_equal<_Ty>> {
using type = conditional_t<_Is_vbool_functor_arg<_Ty>, _Bit_imply, void>;
};

template <class _Ty>
struct _Map_vb_functor<greater_equal<_Ty>> {
using type = conditional_t<_Is_vbool_functor_arg<_Ty>, _Bit_cimply, void>;
};

template <class _Ty>
struct _Map_vb_functor<less<_Ty>> {
using type = conditional_t<_Is_vbool_functor_arg<_Ty>, _Bit_cnimply, void>;
};

template <class _Ty>
struct _Map_vb_functor<greater<_Ty>> {
using type = conditional_t<_Is_vbool_functor_arg<_Ty>, _Bit_nimply, void>;
};

template <class _Ty>
struct _Map_vb_functor<logical_not<_Ty>> {
using type = conditional_t<_Is_vbool_functor_arg<_Ty>, bit_not<>, void>;
Expand Down
45 changes: 45 additions & 0 deletions tests/std/tests/GH_000625_vector_bool_optimization/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,20 @@ CONSTEXPR20 void test_transform_helper(const size_t length) {
bool or_expected_raw[size(source_raw)];
bool xor_expected_raw[size(source_raw)];
bool xnor_expected_raw[size(source_raw)];
bool l_expected_raw[size(source_raw)];
bool le_expected_raw[size(source_raw)];
bool g_expected_raw[size(source_raw)];
bool ge_expected_raw[size(source_raw)];
bool not_expected_raw[size(source_raw)];

transform(begin(source_raw), end(source_raw), begin(source2_raw), begin(and_expected_raw), logical_and<>{});
transform(begin(source_raw), end(source_raw), begin(source2_raw), begin(or_expected_raw), logical_or<>{});
transform(begin(source_raw), end(source_raw), begin(source2_raw), begin(xor_expected_raw), not_equal_to<>{});
transform(begin(source_raw), end(source_raw), begin(source2_raw), begin(xnor_expected_raw), equal_to<>{});
transform(begin(source_raw), end(source_raw), begin(source2_raw), begin(l_expected_raw), less<>{});
transform(begin(source_raw), end(source_raw), begin(source2_raw), begin(le_expected_raw), less_equal<>{});
transform(begin(source_raw), end(source_raw), begin(source2_raw), begin(g_expected_raw), greater<>{});
transform(begin(source_raw), end(source_raw), begin(source2_raw), begin(ge_expected_raw), greater_equal<>{});
transform(begin(source_raw), end(source_raw), begin(not_expected_raw), logical_not<>{});

const vector<bool> source1(source_raw, source_raw + static_cast<ptrdiff_t>(length));
Expand All @@ -89,18 +97,30 @@ CONSTEXPR20 void test_transform_helper(const size_t length) {
vector<bool> or_expected(or_expected_raw, or_expected_raw + static_cast<ptrdiff_t>(length));
vector<bool> xor_expected(xor_expected_raw, xor_expected_raw + static_cast<ptrdiff_t>(length));
vector<bool> xnor_expected(xnor_expected_raw, xnor_expected_raw + static_cast<ptrdiff_t>(length));
vector<bool> less_expected(l_expected_raw, l_expected_raw + static_cast<ptrdiff_t>(length));
vector<bool> less_equal_expected(le_expected_raw, le_expected_raw + static_cast<ptrdiff_t>(length));
vector<bool> greater_expected(g_expected_raw, g_expected_raw + static_cast<ptrdiff_t>(length));
vector<bool> greater_equal_expected(ge_expected_raw, ge_expected_raw + static_cast<ptrdiff_t>(length));
vector<bool> not_expected(not_expected_raw, not_expected_raw + static_cast<ptrdiff_t>(length));

and_expected.resize(length + 3, false);
or_expected.resize(length + 3, false);
xor_expected.resize(length + 3, false);
xnor_expected.resize(length + 3, false);
less_expected.resize(length + 3, false);
less_equal_expected.resize(length + 3, false);
greater_expected.resize(length + 3, false);
greater_equal_expected.resize(length + 3, false);
not_expected.resize(length + 3, false);

vector<bool> and_actual(length + 3);
vector<bool> or_actual(length + 3);
vector<bool> xor_actual(length + 3);
vector<bool> xnor_actual(length + 3);
vector<bool> less_actual(length + 3);
vector<bool> less_equal_actual(length + 3);
vector<bool> greater_actual(length + 3);
vector<bool> greater_equal_actual(length + 3);
vector<bool> not_actual(length + 3);

// Also test combinations of vector<bool>::iterator and vector<bool>::const_iterator for the inputs.
Expand Down Expand Up @@ -155,6 +175,31 @@ CONSTEXPR20 void test_transform_helper(const size_t length) {
// bit_xnor doesn't exist in the Standard
}

{
const auto less_ret = transform(cfirst1, clast1, cfirst2, less_actual.begin(), less<>{});
assert(less_actual == less_expected);
assert(less_ret == less_actual.begin() + static_cast<ptrdiff_t>(length));
}

{
const auto less_equal_ret = transform(cfirst1, clast1, cfirst2, less_equal_actual.begin(), less_equal<>{});
assert(less_equal_actual == less_equal_expected);
assert(less_equal_ret == less_equal_actual.begin() + static_cast<ptrdiff_t>(length));
}

{
const auto greater_ret = transform(cfirst1, clast1, cfirst2, greater_actual.begin(), greater<>{});
assert(greater_actual == greater_expected);
assert(greater_ret == greater_actual.begin() + static_cast<ptrdiff_t>(length));
}

{
const auto greater_equal_ret =
transform(cfirst1, clast1, cfirst2, greater_equal_actual.begin(), greater_equal<>{});
assert(greater_equal_actual == greater_equal_expected);
assert(greater_equal_ret == greater_equal_actual.begin() + static_cast<ptrdiff_t>(length));
}

{
auto not_ret = transform(first1, last1, not_actual.begin(), logical_not<>{});
assert(not_actual == not_expected);
Expand Down