Home > Back-end >  How can I conditionally instantiate a member object to exhibit different behaviors depending on pare
How can I conditionally instantiate a member object to exhibit different behaviors depending on pare

Time:07-01

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);
  • Related