Relative Iterate
by Adrian Hawryluk
This is a addition/replacement of Boost's vertical repetition preprocessor
library iterate. In order to use this library you NEED the Boost library. The
directory bboost (boosting boost) must be placed off of an include directory
that your compiler will use.
The Boost preprocessor library is a great library. However, I've found a
problem that it does not cover. It cannot deal with a repetition of a file
that doesn't either exist in the include path set or relative to that set.
This is because the #include BOOST_PP_ITERATE() tries to include the file to
iterate over from another file in another directory. Since it is in another
directory different from the caller, this requires that the caller is located
somewhere off of one of the include directories or it won't be found. Mostly
this isn't a problem as you can usually add an additional include path to your
compiler's command line. However, I must have found the only IDE on the planet
that doesn't allow for adding command line switches to the compiler's command
line. :/ Another reason that this could be useful is if you prefer to keep your
header file includes relative to each other allowing for easily moving the
related header files from one place to another.
To do this, I have broken up the equivalent of BOOST_PP_ITERATE() into 3
sections, an initialisation, a next and an iterate. Separating it out in this
manner allows for the file to include itself or another file directly, thus
allowing for relative include locations.
This has pretty much the same interface as Boost's iterate library. Equivalent
mappings are as follows:
CORE:
1. BOOST_PP_ITERATE()
No equivalent. The functionality of initialising the iteration variables
and iterating over the file given by that macro has been broken up into 3
separate stages:
a. Initialising the iteration variables after defining
REL_ITERATION_PARAMS_x:
#include REL_ITERATE_INIT()
b. Incrementing to the next iteration value:
#include REL_ITERATE_NEXT()
c. Including the file to iterate over:
#include REL_ITERATE()
NOTE: The file specified by REL_ITERATION_PARAMS_x are capable of being
relative to the file that is doing the including IN ADDITION TO
being relative to any of the include directory set. This was not
possible before.
PARAMETERS:
2. BOOST_PP_FILENAME_x -> REL_FILENAME_x
3. BOOST_PP_ITERATION_LIMITS -> REL_ITERATION_LIMITS
4. BOOST_PP_ITERATION_PARAMS_x -> REL_ITERATION_PARAMS_x
INFORMATION:
5. BOOST_PP_ITERATION_DEPTH() -> REL_ITERATION_DEPTH()
6. No equivalent -> REL_ITERATION_FILE()
7. BOOST_PP_ITERATION_FINISH() -> REL_ITERATION_FINISH()
8. BOOST_PP_ITERATION_FLAGS() -> REL_ITERATION_FLAGS()
9. BOOST_PP_ITERATION_LIMITS -> REL_ITERATION_LIMITS
10. BOOST_PP_ITERATION_START() -> REL_ITERATION_START()
11. BOOST_PP_ITERATION() -> REL_ITERATION()
12. No equivalent -> REL_FRAME_FILE(x)
13. BOOST_PP_FRAME_FINISH(x) -> REL_FRAME_FINISH(x)
14. BOOST_PP_FRAME_FLAGS(x) -> REL_FRAME_FLAGS(x)
15. BOOST_PP_FRAME_ITERATION(x) -> REL_FRAME_ITERATION(x)
16. BOOST_PP_FRAME_START(x) -> REL_FRAME_START(x)
17. BOOST_PP_IS_ITERATING -> REL_IS_ITERATING
18. BOOST_PP_LIMIT_ITERATION -> REL_LIMIT_ITERATION
19. BOOST_PP_LIMIT_ITERATION_DIM -> REL_LIMIT_ITERATION_DIM
Like Boost, REL_ITERATION() is always a boost macro comparable number, no
matter how you defined your start and finish values (i.e. as a constant or an
equation), so long as they have an equated value between 0 and
REL_LIMIT_ITERATION (currently 256) inclusive. Also, the current iteration
depth must be between 1 and REL_LIMIT_ITERATION_DIM (currently 3) inclusive.
REL_ITERATION_START() and REL_ITERATION_FINISH() and the corresponding FRAME
macros have no dependency to what the original passed values did. That means
if you had passed a macro for the start or finish, it would not change from
within the loop if you changed that macro.
However, REL_ITERATION_FILE() and REL_FRAME_FILE() do not break this dependency
(there is no way to do so). Thus, if you had used a macro to pass the file
name, and later changed the value of that macro from within the loop, that
would cause the apparent return value of the FILE() macros to change as well
and could potentially cause problems with looping (unless intended for some
reason) as it would potentially include the wrong file. In fact, this may be
why there is no FILE() like macro exposed in the Boost library. However, I
think that it is more dangerous that the user of the library is not aware of
this then to just sweep this under the rug.
If you have a compiler that doesn't allow files to include themselves, you will
need to make a proxy include file. Boost has one that can be used by including
via the BOOST_PP_INCLUDE_SELF() macro, but again, this cannot deal with
relative directories that are not in the include directory set. If you need to
do this, I'd recommend that you copy the file
boost/preprocessor/iteration/detail/self.hpp somewhere that can be accessed
relative from the file you are iterating from and do this:
# include <bboost/rel_iterate.hpp>
# define REL_ITERATION_PARAMS_1 (3, (1, 3, "self.hpp"))
// NOTE: If self.hpp is not in same directory as file.h, state where
// self.hpp is relative to file.hpp.
# include REL_ITERATE_INIT()
...
# include REL_ITERATE_NEXT()
# define BOOST_PP_INDIRECT_SELF "file.h" // Where file.h is the name of
// your file you're iterating on.
// NOTE: If self.hpp is not in same directory as file.h, state where
// file.hpp is relative to self.hpp.
# include REL_ITERATE()
To see the differences between usage of Boost's ITERATE and my REL_ITERATE
libraries, here's the example taken from the BOOST_PP_ITERATION_DEPTH
documentation:
//FILENAME: file.h
#if !BOOST_PP_IS_ITERATING
#ifndef FILE_H_
#define FILE_H_
#include <boost/preprocessor/iteration/iterate.hpp>
#define BOOST_PP_ITERATION_PARAMS_1 (3, (1, 3, "file.h"))
#include BOOST_PP_ITERATE()
#endif
#elif BOOST_PP_ITERATION_DEPTH() == 1
+ depth BOOST_PP_ITERATION_DEPTH()
// ...
#define BOOST_PP_ITERATION_PARAMS_2 \
(3, (1, BOOST_PP_ITERATION(), "file.h")) \
//--
#include BOOST_PP_ITERATE()
#elif BOOST_PP_ITERATION_DEPTH() == 2
- depth BOOST_PP_ITERATION_DEPTH()
// ...
#endif
Here it is modified to use REL_ITERATE (simple name change differences are
prefixed by a '+', major differences are prefixed with a 'x'):
//FILENAME: file2.h
+ #if !REL_IS_ITERATING
#ifndef FILE_H_
#define FILE_H_
+ #include <bboost/iteration/rel_iterate.hpp>
+ #define REL_ITERATION_PARAMS_1 (3, (1, 3, "file2.hpp"))
x #include REL_ITERATE_INIT()
x #include REL_ITERATE()
#endif
+ #elif REL_ITERATION_DEPTH() == 1
+ + depth REL_ITERATION_DEPTH()
// ...
+ #define REL_ITERATION_PARAMS_2 \
(3, (1, REL_ITERATION(), "file2.hpp")) \
//--
x #include REL_ITERATE_INIT()
x #include REL_ITERATE()
x #elif REL_ITERATION_DEPTH() == 2
+ - depth REL_ITERATION_DEPTH()
// ...
x #include REL_ITERATE_NEXT()
x #include REL_ITERATE()
#endif
Note that at the END of EACH ITERATION, an inclusion of REL_ITERATE() is
REQUIRED. This is because, unlike the BOOST_PP_ITERATE() inclusion, which
handles the recursive file calls automatically, the recursive file calls must
be handled manually through this include. This is what keeps the inclusion
relative. Also, at the end of the last iteration, a REL_ITERATE_NEXT() is
required prior to REL_ITERATE() to move onto the next iteration. That takes
care of switching between different iteration depths as well. An alternative
would be to have this include at thee end of the file after the #endif, but
doing so would require restructuring the #if statements, and I wanted to make
it as close as possible to the original.
Finally, here it is with a copy of self.hpp copied into the same directory as
the iteration file:
//FILENAME: file3.h
#if !REL_IS_ITERATING
#ifndef FILE_H_
#define FILE_H_
#include <bboost/iteration/rel_iterate.hpp>
#define REL_ITERATION_PARAMS_1 (3, (1, 3, "self.hpp"))
#define BOOST_PP_INDIRECT_SELF "file3.h"
#include REL_ITERATE_INIT()
#include REL_ITERATE()
#endif
#elif REL_ITERATION_DEPTH() == 1
+ depth REL_ITERATION_DEPTH() REL_ITERATION()
// ...
#define REL_ITERATION_PARAMS_2 \
(3, (1, REL_FRAME_ITERATION(1), "self.hpp")) \
//--
#define BOOST_PP_INDIRECT_SELF "file3.h"
#include REL_ITERATE_INIT()
#include REL_ITERATE()
#elif REL_ITERATION_DEPTH() == 2
- depth REL_ITERATION_DEPTH() REL_ITERATION()
// ...
#include REL_ITERATE_NEXT()
#include REL_ITERATE()
#endif
If you use the self.hpp method, ensure that you get the one located in
boost/preprocessor/iteration/detail. The one located in that one's parent
directory references the one in detail, so copying the one from the parent
directory will result in a non-relative file inclusion.
It is unfortunate that iterations over files that don't exist relative to the
include directory set are not as easily supported using the preprocessor since
full paths to the file are not available, but they can mostly be dealt with
using the REL_ITERATION library.
As for those compilers that don't allow for direct self includes. This work
around at least that doesn't require copying a large set of the library files
to a relative accessible directory, just a single file.