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

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