Objects with value semantics - enums vs classes

D

David Rasmussen

The problem comes up in all sorts of contexts. But here is an example:

We want to model the basics of chess, maybe for making a chess program,
or maybe just to make an interactive board or ...

We have to have the notions of colors, squares and pieces represented.
Ideally, we want to be able to do things like initializing the board:

void initial()
{
const Piece backLine[] =
{rook,knight,bishop,queen,king,bishop,knight,rook};

toMove = white;
int i = 0;
for (square s = a1; s <= h1; ++s)
{
board.piece = backLine[i++];
board.color = white;
}
for (square s = a2; s <= h2; ++s)
{
board.piece = pawn;
board.color = white;
}
for (square s = a3; s <= h6; ++s)
board.piece = none;
for (square s = a7; s <= h7; ++s)
{
board.piece = pawn;
board.color = black;
}
i = 0;
for (Square s = a8; s <= h8; ++s)
{
board.piece = backLine[i++];
board.color = black;
}
}

This is a simple function that has to be present in a program involving
chess, and it shows a little bit about what is needed.

The simplest way to do this, is by making enums for piece, color, and
square, like:

enum Color {white,black};
enum Piece {none,pawn,knight,bishop,queen,king}
enum Square {a1,a2,...,h8};

But already here, we are having trouble. We have to define several
functions to get Square to work as indicated above.

We could do it even simpler and less typesafe by just using ints. Then
we would have our value semantics; assignment, comparison,
incrementation etc.

And we could make everything a class. But how?
Then one would have to go through the tedious process of defining all
the operators necesary for value semantics.

In SML, one would just do

datatype Piece = none | pawn | ... | king;

(or something like that... My SML is rusty).

In Ada, one can do something similarly.

This sort of thing, defining a collection of values of the same kind,
being able to assign them, and iterate through them etc., but as typed
_values_, not as ints, is pretty essential as I see it.

I don't really know what my question is :)
Maybe this:

What is the _intended_ C++ way of solving this _kind_ or _class_ or
_type_ of problem?
What is the kosher OOP way to do it?

I know that one can make a hierachy like

class Color
{};

class White : public Color
{}

class Black : public Color
{}

or whatever...

But then, how will the above function look? I can't do

toMove = white;

then.

In short:

What would be a nice flexible and (type)safe design of such things?
Please give an example of how to define Color, Piece and Square, and an
outline of what the initial() function will look like.

I think this kind of problem occurs all the time.

/David
 
D

David Fisher

David Rasmussen said:
This sort of thing, defining a collection of values of the same kind,
being able to assign them, and iterate through them etc., but as typed
_values_, not as ints, is pretty essential as I see it.

What is the _intended_ C++ way of solving this _kind_ or _class_ or
_type_ of problem?
What is the kosher OOP way to do it?

There is an article on various things you can do with enums called "Stupid
Enumeration Tricks" (don't be put off by the title) in:

http://www.edm2.com/0405/enumeration.html

Hope this is helpful,

David F
 
G

Gianni Mariani

David said:
The problem comes up in all sorts of contexts. But here is an example:

We want to model the basics of chess, maybe for making a chess program,
or maybe just to make an interactive board or ...

We have to have the notions of colors, squares and pieces represented.
Ideally, we want to be able to do things like initializing the board:

void initial()
{
const Piece backLine[] =
{rook,knight,bishop,queen,king,bishop,knight,rook};

toMove = white;
int i = 0;
for (square s = a1; s <= h1; ++s)
{
board.piece = backLine[i++];
board.color = white;
}
for (square s = a2; s <= h2; ++s)
{
board.piece = pawn;
board.color = white;
}
for (square s = a3; s <= h6; ++s)
board.piece = none;
for (square s = a7; s <= h7; ++s)
{
board.piece = pawn;
board.color = black;
}
i = 0;
for (Square s = a8; s <= h8; ++s)
{
board.piece = backLine[i++];
board.color = black;
}
}

This is a simple function that has to be present in a program involving
chess, and it shows a little bit about what is needed.

The simplest way to do this, is by making enums for piece, color, and
square, like:

enum Color {white,black};
enum Piece {none,pawn,knight,bishop,queen,king}
enum Square {a1,a2,...,h8};



#include <string>

class Color;
extern Color white;
extern Color black;

class Piece;
extern Piece pawn,knight,bishop,queen,king,rook;

class Square;
// I have no idea why you would want to enumerate these.

std::string Name( const Color & );
std::string Name( const Piece & );

inline bool operator==( const Color & b, const Color & a )
{
return &b == &a;
}

inline bool operator==( const Piece & b, const Piece & a )
{
return &b == &a;
}

inline bool operator<( const Color & b, const Color & a )
{
return &b < &a;
}

inline bool operator<( const Piece & b, const Piece & a )
{
return &b < &a;
}
In short:

What would be a nice flexible and (type)safe design of such things?
Please give an example of how to define Color, Piece and Square, and an
outline of what the initial() function will look like.

I would probably design it differently to what you have above.

I's add

class PlayingPiece;


extern PlayingPiece white_pawn;
extern PlayingPiece black_pawn;
extern PlayingPiece white_rook;
extern PlayingPiece black_rook;
.... etc

Then I would set up a static array:



struct BoardPositions
{
PlayingPiece * board_array[8][8];
};

BoardPositions initial_positions =
{
{
{ & black_rook, &black_knight, &black_bishop, ... etc
( & black_pawn, & black_pawn, & black_pawn, & black_pawn, ...
{ },
{ },
{ },
{ },
{ & white_pawn, & white_pawn, & white_pawn, & white_pawn, ...
{ & white_rook, & white_night, .... etc
}
};

struct Board
{
BoardPositions m_board_positions

Board()
m_board_positions( initial_positions )
{
}
}

I think this kind of problem occurs all the time.

I'd almost never use enums. Usually you're better off statically
creating an object that represents the concept.

.... I hope this helps.
 
D

David Rasmussen

David said:
There is an article on various things you can do with enums called "Stupid
Enumeration Tricks" (don't be put off by the title) in:

http://www.edm2.com/0405/enumeration.html

Hope this is helpful,

Thanks, it is!

And it also highlights some very basic problems that C++ has.

Let's take the simple example mentioned on that page: the days of the week.

Who would make a class for that, and make "monday", "tuesday" etc. be
instances of this class? Sometimes, you want to enumerate something, and
you know you'll never need to expand it or reuse it etc. In my opinion,
such "native" but smart enums, should be in the language for exactly the
same reason that ints and doubles should: they are very basic.
They should be typesafe (no implicit to-int conversion), have value
semantics, and they should support native iteration (for, while etc.) like:

for (weekday w = monday; w != sunday; ++w)
...;

Ada and a lot of other languages have this already. Bjarne Stroustrup's
view on this seems to be that we wouldn't like "too good" enums, because
then people would use them too much when an OOP approach would be better.

/David
 
D

David Rasmussen

The really funny thing is, that this article is 8 years old. And it
still touches upon some important core topics of C++.

/David
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top