C++ Logo

std-proposals

Advanced search

[std-proposals] Add std::shift_left and std::shift_right as function objects

From: Leon Lenk <leonlenk_at_[hidden]>
Date: Thu, 27 Jul 2023 21:16:01 -0600
Earlier we found ourselves writing code for a bitmap based chess engine
which when finding pseudo-legal moves required bit shifting in different
directions based on the color of the pawn.

#include <cstdint>

void getLegalChessMoves(bool isForWhite, Board* board) {

  uint64_t attacks, pushes;

  for (uint64_t mask = /*...*/) {

    if (isForWhite) {

      attacks = ((mask << 9) | (mask << 7));

      pushes = (mask << 8) & board->emptySquares;

       // ...

    } else {

      attacks = ((mask >> 9) | (mask >> 7));

      pushes = (mask >> 8) & board->emptySquares;

      // ...

    }

  }

}


One way of deduplicating this would be to treat the bit shifts as function
objects and switch the used function depending on the isForWhite condition,
like so:


#include <cstdint>

#include <functional>

void getLegalChessMoves(bool isForWhite, Board* board) {

  uint64_t attacks, pushes;

  auto shift = isForWhite ? std::function<uint64_t(uint64_t,
uint64_t)>(std::shift_left<>()) : std::function<uint64_t(uint64_t,
uint64_t)>(std::shift_right<>());

  for (uint64_t mask = /*...*/) {

      attacks = (shift(mask, 9) | shift(mask, 7));

      pushes = shift(mask, 8) & board->emptySquares;

      // ...

  }

}

However, we were surprised to find that there is no functional object for
left and right bit shifts in <functional>, even though every other binary
operator in C++ has a corresponding functional object! A workaround for
this is of course to define the function ourselves:

auto shift = isForWhite ? [](uint64_t a, uint64_t b) { return a << b; } :
[](uint64_t a, uint64_t b) { return a >>b; };

…but such a lambda can already be used in place of any binary operator
functor, and the <functional> header feels incomplete without an
implementation of the std::shift_left and std::shift_right functors. We
propose adding the aforementioned functors with a similar implementation to
the currently defined binary operator functors.

Proposed Changes:

To 22.10.11 [function.objects], Function objects, paragraph 2, add to the
header <functional> synopsis:

template <class T = void> struct shift_left;

template <class T = void> struct shift_right;

template<> struct shift_left<void>;

template<> struct shift_right<void>;

The library provides basic function object classes for all of the bitwise
operators in the language ([expr.shift] 7.6.7)

template <class T> struct shift_left {

  T operator()(const T&x , const T&y ) const;

};

constexpr T operator()(const T& x, const T& y) const;

operator() returns x << y

template <class T> struct shift_right {

  T operator()(const T&x , const T&y ) const;

};

constexpr T operator()(const T& x, const T& y) const;

operator() returns x >> y

Cheers,

~ Andrew and Leon

Received on 2023-07-28 03:16:13