I'm creating two objects of struct Player
, each with a pawn
container. I want this member to be defined with a different type depending on the Player
object it is in, such that white.pawn.say()
and black.pawn.say
exhibit different behaviors. I've tried making WhitePawn
and BlackPawn
classes respectively, but I don't know of a factory design pattern that will allow me to instantiate member objects inside of a class.
template <bool N> struct Pawn : public Piece {
Pawn(U64 bb) : Piece(bb, 1) {};
};
template <> struct Pawn<0> : public Piece {
void say(U64 x) {
std::cout << "White pawns" << (x << 8) << "\n";
}
};
template <> struct Pawn<1> : public Piece {
void say(U64 x) {
std::cout << "Black pawns" << (x >> 8) << "\n";
}
};
struct Player {
Pawn<color> pawn; // Expected compile-time constant expression
Knight knight;
Bishop bishop;
Player(U64 p, U64 n, U64 b, bool color)
: pawn(p)
, knight(n)
, bishop(b)
{};
};
Player white(
0xFF000000000000, // pawns
0x4200000000000000, // knights
0x2400000000000000, // bishops
0 // color (white)
);
Player black(
0xFF00, // pawns
0x42, // knights
0x24, // bishops
1 // color (black)
);
I don't want to make Player
a templated struct because it is called as a datatype later on. (int makeMove(Player& own, Player& enemy, U16 move) {...
) Is there perhaps a way to make convert color
to a constant expression so the compiler picks up on it? What's the best way to go about conditionally instantiating member objects of different types for different parent objects?
In your case, you're sure that there are only a given number of possible classes to choose from. This is called discriminated union in type systems and can be represented using a variant:
Example code:
struct Player {
using PawnType = std::variant<Pawn<0>, Pawn<1>>;
PawnType pawn;
Knight knight;
Bishop bishop;
Player(U64 p, U64 n, U64 b, bool color)
: pawn(color ? PawnType(Pawn<1>(p)) : PawnType(Pawn<0>(p))
, knight(n)
, bishop(b)
{};
};
Usage of pawn
at this point requires writing a visitor:
auto v = [&](auto&& pawn) {
/* here goes the code accessing the actual Pawn<0> or Pawn<1> as pawn */
};
std::visit(v, player.pawn);
E.g., instead of white.pawn.say(x);
, you'd write:
auto v = [&](auto&& actualPawn) {
actualPawn.say(x);
}; // this is a visitor
std::visit(v, white.pawn);
or, in case of such a small visitor, you can write in one line:
std::visit([&](auto&& actualPawn) { actualPawn.say(x) }, white.pawn);