Evaluated Slots

The evaluated slot mechanism is a tool to fully evaluate a constant integral expression and avoid the lazy evaluation normally performed by the preprocessor.

Tutorial

In order to understand the use of such a mechanism, I will start with a simple file-iteration example.  Consider the following scenario....
for (int i = 0; i < 10; ++i) {
   for (int j = 0; j < i; ++j) {
      // ... use i and j
   }
}
The above is a simple runtime model of the following multidimensional file-iteration....
// file.hpp
#if !BOOST_PP_IS_ITERATING
   #ifndef FILE_HPP_
   #define FILE_HPP_

   #include <boost/preprocessor/iteration/iterate.hpp>

   #define BOOST_PP_ITERATION_PARAMS_1 (3, (0, 9, "file.hpp"))
   #include BOOST_PP_ITERATE()

   #endif // FILE_HPP_
#elif BOOST_PP_ITERATION_DEPTH() == 1
   #define I BOOST_PP_ITERATION()

   #define BOOST_PP_ITERATION_PARAMS_2 (3, (0, I, "file.hpp"))
   #include BOOST_PP_ITERATE()

   #undef I
#elif BOOST_PP_ITERATION_DEPTH() == 2
   #define J BOOST_PP_ITERATION()

   // use I and J

   #undef J
#endif
There is a problem with the code above.  The writer expected I to refer the previous iteration frame.  However, that is not the case.  When the user refers to I, he is actually referring to BOOST_PP_ITERATION(), not the value of BOOST_PP_ITERATION() at the point of definition.  Instead, it refers to exactly the same value to which J refers.
The problem is that the preprocessor always evaluates everything with lazy evaluation.  To solve the problem, we need I to be evaluated here:
// ...
#elif BOOST_PP_ITERATION_DEPTH() == 1
   #define I BOOST_PP_ITERATION()
// ...
Fortunately, the library offers a mechanism to do just that:  evaluated slots.  The following code uses this mechanism to "fix" the example above...
// ...
#elif BOOST_PP_ITERATION_DEPTH() == 1
   #define BOOST_PP_VALUE BOOST_PP_ITERATION()
   #include BOOST_PP_ASSIGN_SLOT(1)
   #define I BOOST_PP_SLOT(1)
// ...
There are two steps to the assignment of an evaluated slot.  First, the user must define the named external argument BOOST_PP_VALUE.  This value must be an integral constant expression.  Second, the user must include BOOST_PP_ASSIGN_SLOT(x), where x is the particular slot to be assigned to (1 to BOOST_PP_LIMIT_SLOT_COUNT).  This will evaluate BOOST_PP_VALUE and assign the result to the slot at index x.
To retrieve a slot's value, the user must use BOOST_PP_SLOT(x).
In the case above, I is still lazily evaluated.  However, it now evaluates to BOOST_PP_SLOT(1).  This value will not change unless there is a subsequent call to BOOST_PP_ASSIGN_SLOT(1).

Advanced Techniques

The slot mechanism can also be used to perform calculations:
#include <iostream>

#include <boost/preprocessor/slot/slot.hpp>
#include <boost/preprocessor/stringize.hpp>

#define X() 4

#define BOOST_PP_VALUE 1 + 2 + 3 + X()
#include BOOST_PP_ASSIGN_SLOT(1)

#undef X

int main(void) {
   std::cout
      << BOOST_PP_STRINGIZE(BOOST_PP_SLOT(1))
      << &std::endl;
   return 0;
}
In essence, anything that can be evaluated in an #if (or #elif) preprocessor directive is available except the defined operator.
It is even possible to use a particular slot itself while reassigning it:
#define BOOST_PP_VALUE 20
#include BOOST_PP_ASSIGN_SLOT(1)

#define BOOST_PP_VALUE 2 * BOOST_PP_SLOT(1)
#include BOOST_PP_ASSIGN_SLOT(1)

BOOST_PP_SLOT(1) // 40

See Also

- Paul Mensonides

© Copyright Housemarque Oy 2002
© Copyright Paul Mensonides 2002

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at www.boost.org/LICENSE_1_0.txt)