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?
CodePudding user response:
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);