C++ templates question

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Jose Carlos
Posts: 151
Joined: Wed Mar 08, 2006 10:09 pm
Location: Murcia (Spain)

C++ templates question

Post by Jose Carlos »

Hi. After a long time out of the business, I'm writing a new engine from scratch. I'm an old fashion C guy trying to learn C++ on the fly while writing the engine, so I apologize in advance if my question is damn stupid.

If found templates very useful in Stockfish (yes, I admit, I've copied code from Stockfish: the word "template"), and I've googled to try to understand how they work. I'm not sure, but it seems they can't be used the way I'm trying to, but I don't quite understand why. If this is the case, I'd appreciate an advice on how to do it in a different way.

The code:
I have a CColor class, which defines an enum that I typedef as TColores:

Code: Select all

public:
  static enum eColor
  {
    BLANCAS = 0,
    NEGRAS = 1,
    NEUTRAL = 2
  };
  typedef enum eColor TColores;
In my position class, in the header file, I have the following declaration:

Code: Select all

  template <CColor&#58;&#58;TColores tColor>
  inline void InsertarJugadasPeon&#40;CJugada * pJug, const CCasilla & cDesde&#41;;
That's a function I use to insert pawn moves, which is called from the GenMov function of the position class.

In the appropiate code file, I have the body of the function:

Code: Select all

template <CColor&#58;&#58;TColores tColor>
inline void CPosicion&#58;&#58;InsertarJugadasPeon&#40;CJugada * pJug, const CCasilla & cDesde&#41;
&#123;
  // Code here
&#125;
(CJugada is the Move class, CCasilla is the Square class, and Desde is From).

When I compile (MS Visual Studio 2008 Express Edition) I get this error:

Code: Select all

Compilando...
casilla.cpp
globales.cpp
main.cpp
posicion.cpp
posicion_ataques.cpp
posicion_genmov.cpp
posicion_mover.cpp
reloj.cpp
sesion.cpp
wbii.cpp
busqueda.cpp
Generando código...
d&#58;\proyectoschz\anubis\posicion_genmov.cpp&#40;47&#41; &#58; fatal error C1001&#58; Error interno en el compilador.
Archivo del compilador 'f&#58;\dd\vctools\compiler\utc\src\p2\main.c&#91;0x5111E0DB&#58;0x00000030&#93;', línea 182
 Para solucionar este problema, intente simplificar o cambiar el programa en aquellas líneas próximas a las ubicaciones que se enumeran a continuación.
Elija el comando Soporte técnico en el menú Ayuda de Visual C++ 
 o abra el archivo de ayuda de soporte técnico para obtener más información
Can you tell me what am I doing wrong? What's the correct way to do it?
__________________________
José Carlos Martínez Galán
Jose Carlos
Posts: 151
Joined: Wed Mar 08, 2006 10:09 pm
Location: Murcia (Spain)

Re: C++ templates question

Post by Jose Carlos »

Sorry for the double posting. It seems that the problem is in the calling function (GenMov).
When I use:

Code: Select all

InsertarJugadasPeon<cPieza.GetColorPieza&#40;).m_tValor>&#40;pJug, *pCasDesde&#41;;
The error happens, while if I use the constant:

Code: Select all

InsertarJugadasPeon<CColor&#58;&#58;BLANCAS>&#40;pJug, *pCasDesde&#41;;
It compiles fine. Is this normal?
__________________________
José Carlos Martínez Galán
kbhearn
Posts: 411
Joined: Thu Dec 30, 2010 4:48 am

Re: C++ templates question

Post by kbhearn »

Edit: nevermind, your second post is clearer - the problem is that template parameters must be compile-time constants.
Jose Carlos
Posts: 151
Joined: Wed Mar 08, 2006 10:09 pm
Location: Murcia (Spain)

Re: C++ templates question

Post by Jose Carlos »

kbhearn wrote:Edit: nevermind, your second post is clearer - the problem is that template parameters must be compile-time constants.
Thanks ;)
__________________________
José Carlos Martínez Galán
Gerd Isenberg
Posts: 2250
Joined: Wed Mar 08, 2006 8:47 pm
Location: Hattingen, Germany

Re: C++ templates question

Post by Gerd Isenberg »

José Carlos wrote:Sorry for the double posting. It seems that the problem is in the calling function (GenMov).
When I use:

Code: Select all

InsertarJugadasPeon<cPieza.GetColorPieza&#40;).m_tValor>&#40;pJug, *pCasDesde&#41;;
The error happens, while if I use the constant:

Code: Select all

InsertarJugadasPeon<CColor&#58;&#58;BLANCAS>&#40;pJug, *pCasDesde&#41;;
It compiles fine. Is this normal?
Yes. Templates require parameters determined at compile time.
Jose Carlos
Posts: 151
Joined: Wed Mar 08, 2006 10:09 pm
Location: Murcia (Spain)

Re: C++ templates question

Post by Jose Carlos »

Gerd Isenberg wrote: Yes. Templates require parameters determined at compile time.
Thanks. I understand. I guess the compiler could have been more informative however... :)
__________________________
José Carlos Martínez Galán
Jose Carlos
Posts: 151
Joined: Wed Mar 08, 2006 10:09 pm
Location: Murcia (Spain)

Some advice needed

Post by Jose Carlos »

This is a translation of my code (in spanish) for the InsertPawnMoves function. As this is my first attemp with templates, I wonder if some expert here could tell me if this is the way templates should be used in order to save duplicate code, or if this is just crap coding from a C man :) :

Code: Select all

template <CColor&#58;&#58;TColores tColor>
inline void CPosition&#58;&#58;InsertPawnMoves&#40;CMove * pMove, const CSquare & cFrom&#41;
&#123;
  CSquare cTo;
  CSquare&#58;&#58;TDirections tAdvanceOne = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP &#58; CSquare&#58;&#58;DOWN&#41;;
  CSquare&#58;&#58;TRows tRowPromo         = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;ROW_8 &#58; CSquare&#58;&#58;ROW_1&#41;;
  CSquare&#58;&#58;TRows tRowBase          = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;ROW_2 &#58; CSquare&#58;&#58;ROW_7&#41;;
  CSquare&#58;&#58;TDirecions tCaptLeft    = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP_LEFT &#58; CSquare&#58;&#58;DOWN_LEFT&#41;;
  CSquare&#58;&#58;TDirecions tCaptRight   = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP_RIGHT &#58; CSquare&#58;&#58;DOWN_RIGHT&#41;;

  assert &#40;tColor == CColor&#58;&#58;WHITE || tColor == CColor&#58;&#58;BLACK&#41;;

  // Advances
  cTo = cFrom + tAdvanceOne;
  if &#40;m_cBoard&#91;cTo.m_tValue&#93; == CPiece&#58;&#58;EMPTY&#41;
  &#123;
    if &#40;cTo.GetRow&#40;) == tRowPromo&#41;
      // Advance 1 - promotion
      InsertPromotions<tColor>&#40;pMove++, cFrom, cTo&#41;;
    else
    &#123;
      // Advance 1 - normal
      InsertMove&#40;pMove++, cFrom, cTo&#41;;

      // Advance 2
      if &#40;cFrom.GetRow&#40;) == tRowBase&#41;
      &#123;
        cTo += tAdvanceOne;
        if &#40;m_cBoard&#91;cTo.m_tValue&#93; == CPiece&#58;&#58;EMPTY&#41;
          InsertMove&#40;pMove++, cFrom, cTo&#41;;
      &#125;
    &#125;
  &#125; // if &#40;m_cBoard&#91;cTo.m_tValue&#93; == CPiece&#58;&#58;EMPTY&#41;

  // Captures
  cTo = cFrom + tCaptLeft;
  if &#40;cTo.InBoard&#40;))
    InsertPawnCapture<tColor>&#40;pMove++, cFrom, cTo&#41;;
  cTo = cFrom + tCaptRight;
  if &#40;cTo.InBoard&#40;))
    InsertPawnCapture<tColor>&#40;pMove++, cFrom, cTo&#41;;
&#125;
Thanks in advance.
__________________________
José Carlos Martínez Galán
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Some advice needed

Post by mcostalba »

José Carlos wrote:This is a translation of my code (in spanish) for the InsertPawnMoves function. As this is my first attemp with templates, I wonder if some expert here could tell me if this is the way templates should be used in order to save duplicate code, or if this is just crap coding from a C man :) :
Hi José,

I think your use is fine, btw in SF pawn moves generation templates are used in a very similar way, the only little detail is that you really want following "variables" to actually be compile time constants:

Code: Select all

  CSquare&#58;&#58;TDirections tAdvanceOne = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP &#58; CSquare&#58;&#58;DOWN&#41;;
  CSquare&#58;&#58;TRows tRowPromo         = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;ROW_8 &#58; CSquare&#58;&#58;ROW_1&#41;;
  CSquare&#58;&#58;TRows tRowBase          = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;ROW_2 &#58; CSquare&#58;&#58;ROW_7&#41;;
  CSquare&#58;&#58;TDirecions tCaptLeft    = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP_LEFT &#58; CSquare&#58;&#58;DOWN_LEFT&#41;;
  CSquare&#58;&#58;TDirecions tCaptRight   = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP_RIGHT &#58; CSquare&#58;&#58;DOWN_RIGHT&#41;;
So I would put a const qualifier in front of them, like:

Code: Select all

const CSquare&#58;&#58;TDirections tAdvanceOne = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP &#58; CSquare&#58;&#58;DOWN&#41;;
The compiler perhaps is smart enough to find by itself anyhow it is the correct way to const qualify what you want to be substituted in your code at compile time (in C you probably use #define to reach the same goal).
Jose Carlos
Posts: 151
Joined: Wed Mar 08, 2006 10:09 pm
Location: Murcia (Spain)

Re: Some advice needed

Post by Jose Carlos »

mcostalba wrote:
José Carlos wrote:This is a translation of my code (in spanish) for the InsertPawnMoves function. As this is my first attemp with templates, I wonder if some expert here could tell me if this is the way templates should be used in order to save duplicate code, or if this is just crap coding from a C man :) :
Hi José,

I think your use is fine, btw in SF pawn moves generation templates are used in a very similar way, the only little detail is that you really want following "variables" to actually be compile time constants:

Code: Select all

  CSquare&#58;&#58;TDirections tAdvanceOne = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP &#58; CSquare&#58;&#58;DOWN&#41;;
  CSquare&#58;&#58;TRows tRowPromo         = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;ROW_8 &#58; CSquare&#58;&#58;ROW_1&#41;;
  CSquare&#58;&#58;TRows tRowBase          = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;ROW_2 &#58; CSquare&#58;&#58;ROW_7&#41;;
  CSquare&#58;&#58;TDirecions tCaptLeft    = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP_LEFT &#58; CSquare&#58;&#58;DOWN_LEFT&#41;;
  CSquare&#58;&#58;TDirecions tCaptRight   = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP_RIGHT &#58; CSquare&#58;&#58;DOWN_RIGHT&#41;;
So I would put a const qualifier in front of them, like:

Code: Select all

const CSquare&#58;&#58;TDirections tAdvanceOne = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP &#58; CSquare&#58;&#58;DOWN&#41;;
The compiler perhaps is smart enough to find by itself anyhow it is the correct way to const qualify what you want to be substituted in your code at compile time (in C you probably use #define to reach the same goal).
Exactly, now everything makes sense. Thanks!
__________________________
José Carlos Martínez Galán
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Some advice needed

Post by Sven »

José Carlos wrote:This is a translation of my code (in spanish) for the InsertPawnMoves function. As this is my first attemp with templates, I wonder if some expert here could tell me if this is the way templates should be used in order to save duplicate code, or if this is just crap coding from a C man :) :

Code: Select all

template <CColor&#58;&#58;TColores tColor>
inline void CPosition&#58;&#58;InsertPawnMoves&#40;CMove * pMove, const CSquare & cFrom&#41;
&#123;
  CSquare cTo;
  CSquare&#58;&#58;TDirections tAdvanceOne = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP &#58; CSquare&#58;&#58;DOWN&#41;;
  CSquare&#58;&#58;TRows tRowPromo         = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;ROW_8 &#58; CSquare&#58;&#58;ROW_1&#41;;
  CSquare&#58;&#58;TRows tRowBase          = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;ROW_2 &#58; CSquare&#58;&#58;ROW_7&#41;;
  CSquare&#58;&#58;TDirecions tCaptLeft    = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP_LEFT &#58; CSquare&#58;&#58;DOWN_LEFT&#41;;
  CSquare&#58;&#58;TDirecions tCaptRight   = &#40;tColor==CColor&#58;&#58;WHITE ? CSquare&#58;&#58;UP_RIGHT &#58; CSquare&#58;&#58;DOWN_RIGHT&#41;;

  assert &#40;tColor == CColor&#58;&#58;WHITE || tColor == CColor&#58;&#58;BLACK&#41;;

  // Advances
  cTo = cFrom + tAdvanceOne;
  if &#40;m_cBoard&#91;cTo.m_tValue&#93; == CPiece&#58;&#58;EMPTY&#41;
  &#123;
    if &#40;cTo.GetRow&#40;) == tRowPromo&#41;
      // Advance 1 - promotion
      InsertPromotions<tColor>&#40;pMove++, cFrom, cTo&#41;;
    else
    &#123;
      // Advance 1 - normal
      InsertMove&#40;pMove++, cFrom, cTo&#41;;

      // Advance 2
      if &#40;cFrom.GetRow&#40;) == tRowBase&#41;
      &#123;
        cTo += tAdvanceOne;
        if &#40;m_cBoard&#91;cTo.m_tValue&#93; == CPiece&#58;&#58;EMPTY&#41;
          InsertMove&#40;pMove++, cFrom, cTo&#41;;
      &#125;
    &#125;
  &#125; // if &#40;m_cBoard&#91;cTo.m_tValue&#93; == CPiece&#58;&#58;EMPTY&#41;

  // Captures
  cTo = cFrom + tCaptLeft;
  if &#40;cTo.InBoard&#40;))
    InsertPawnCapture<tColor>&#40;pMove++, cFrom, cTo&#41;;
  cTo = cFrom + tCaptRight;
  if &#40;cTo.InBoard&#40;))
    InsertPawnCapture<tColor>&#40;pMove++, cFrom, cTo&#41;;
&#125;
Thanks in advance.
Hi José,

some points:

1) I hope your InsertPawnCapture() method also covers promotions with capture, otherwise you'd better insert that part into InsertPawnMoves().

2) The assert() for tColor to be either white or black should be moved to the very beginning of the function. C++, as opposed to C, does not require all declarations to appear before the first statement, so you can start with an assert() before accessing the "assert-ed" parameters in the initializer expression of a variable declaration.

3) If you use constants like tAdvanceOne, tRowPromo etc. at several places in the move generator, or even in the whole program, you might consider to provide static inline template functions (maybe in some Board class) returning the appropriate color-dependent value based on accessing an (also static) array of two elements, as in this example:

Code: Select all

// header file&#58; declaration

class CBoard &#123;
public&#58;
    static template <CColor&#58;&#58;TColores tColor> advanceOne&#40;);
private&#58;
    static CSquare&#58;&#58;TDirections tAdvanceOne&#91;2&#93;;
&#125;;

// static
inline template <CColor&#58;&#58;TColores tColor> CBoard&#58;&#58;advanceOne&#40;)
&#123;
    return tAdvanceOne&#91;tColor&#93;;
&#125;

// cpp file&#58; definition of static variables

// static
CSquare&#58;&#58;TDirections tAdvanceOne&#91;2&#93; = &#123;
    CSquare&#58;&#58;UP, CSquare&#58;&#58;DOWN
&#125;;

// used somewhere&#58;

cTo = cFrom + CBoard&#58;&#58;advanceOne<tColor>();
There is a tiny overhead of typing few more characters but you save repeating all the same code to define and initialize those constants in each function that uses them. The compiler will replace the function call with the correct array element without any overhead since the array index is known at compile time and the array is static, i.e. not part of any object instance and has therefore a fixed address.

As I said, my proposal is not suitable if this is the only place where you use those constants.

Sven