Here's a fun piece of C++11 code that allows a very flexible definition of a Position aggregate with a variable number of data members, without having to rewrite tedious get()/set() functions whenever you change the class definition, and while still calling make(Move) on all the basic building blocks!
#include <cstddef>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <type_traits>
#include <tuple>
// bitboard representation of piece-sets
struct BB
{
uint64_t data_;
explicit BB(uint64_t d): data_(d) {}
template<typename Move>
void make(Move const& /* m */)
{
std::cout << "updating BB" << "\n";
}
};
// side to move
struct Color
{
bool data_;
explicit Color(bool d): data_(d) {}
template<typename Move>
void make(Move const& /* m */)
{
std::cout << "updating Color" << "\n";
}
};
namespace detail {
template<typename Move, std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
do_make(Move&& /*m*/, std::tuple<Tp...>& /*t*/)
{
// no-op for empty tuples
}
template<typename Move, std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
do_make(Move&& m, std::tuple<Tp...>& t)
{
std::get<I>(t).make(std::forward<Move>(m));
// recurse to next data member
do_make<Move, I + 1, Tp...>(std::forward<Move>(m), t);
}
} // namespace detail
template<typename... Types>
class Aggregate
{
private:
// the data
std::tuple<Types...> data_;
public:
// forward all constructor arguments to the std::tuple constructor
template<typename... Args>
Aggregate(Args&&... args)
:
data_(std::forward<Args>(args)...)
{}
// aggregation read-semantics: member-by-member
template<std::size_t I>
auto get() const -> decltype(std::get<I>(data_))
{
return std::get<I>(data_);
}
// transactional write-semantics: all members at once
template<typename Move>
void make(Move&& m)
{
detail::do_make(std::forward<Move>(m), data_);
}
};
// define accessor names
enum: int { black_pieces, white_pieces, kings, color };
enum: bool { black, white };
typedef int Move;
// define Position type
typedef Aggregate<BB, BB, BB, Color> Position;
int main()
{
Position p { BB{0xF0}, BB{0x0F}, BB{0x0}, Color{white} };
std::cout << std::hex;
// can read every member separately
std::cout << std::setw(3) << p.get<black_pieces>().data_ << "\n";
std::cout << std::setw(3) << p.get<white_pieces>().data_ << "\n";
std::cout << std::setw(3) << p.get<kings>().data_ << "\n";
std::cout << p.get<color>().data_ << "\n";
// can only update all members at once
Move move{0};
p.make(move);
}
It works on gcc 4.7.2, Clang 3.2, Intel 13.0. The trick uses variadic templates, initializer lists, type deduction and move semantics.
The main idea is that a Position is considered a tuple aggregate with respect to reads (e.g. during eval/move generation), and a single transactional entity with respect to writes (i.e. when making moves). If every tuple member has a make(Move) member function, then the Aggregate class template will automatically call all of them (using variadic template recursion). Users only have to define which basic building blocks are present in the Position, and the get() member template will automatically extract each block when indexed with the appropriate number (which the enum has named for readability). Also the Position constructor template will forward all arguments to the constructors of the building blocks.