2009 Apr 26 - Sun
Boost Preprocessor: Arrays
Typically, in some form of C++ best practice summaries, it is recommended to stay away
from using the C++ Macro Preprocessor. For the most part, except when I needed to
Microsoft
MFC message maps, where use preprocessor macros, I have followed this maxim. Until now.
I came across a situation where one section of code is dependent upon the order of
declarations in another section of code. With manual code preparation, and even if things
are documented appropriately, it is easy to forget to update the inter-related sections of
code properly.
An example is when initializing the column definitions of an MFC CListView. I'd like to
construct an enumeration of column indexes and ensure those remain in-sync with any changes
I may make to the CListView column defintions themselves.
I hadn't realized the power of the C++ macro preprocessor until I started reading
Appendix A: An Introduction to Preprocessor Metaprogramming in the book "C++ Template
Metaprogramming" by David Abrahams and Aleksey Gurtovoy.
By using the
Boost Preprocessor Library, the power of the C++ Macro Preprocessor is realized.
I can now define my column structures and associated variables in a single header file.
I also define various extraction macros.
#include "boost/preprocessor/tuple/elem.hpp"
#include "boost/preprocessor/array/elem.hpp"
#include "boost/preprocessor/array/size.hpp"
#include "boost/preprocessor/punctuation/comma_if.hpp"
#include "boost/preprocessor/repetition/repeat.hpp"
#define COLHDR_DELTAS_ARRAY_ELEMENT_SIZE 6
#define COLHDR_DELTAS_ARRAY \
(15, \
( \
(COLHDR_DELTAS_COL_UndSym, "UndSym", LVCFMT_LEFT, 50, std::string, m_sSymbolUnderlying), \
(COLHDR_DELTAS_COL_Sym , "Sym", LVCFMT_RIGHT, 50, std::string, m_sSymbol), \
(COLHDR_DELTAS_COL_Strk , "Strk", LVCFMT_RIGHT, 50, double, m_dblStrike), \
(COLHDR_DELTAS_COL_Expiry, "Expiry", LVCFMT_RIGHT, 50, ptime, m_dtExpiry), \
(COLHDR_DELTAS_COL_Bid , "Bid", LVCFMT_RIGHT, 50, double, m_dblBid), \
(COLHDR_DELTAS_COL_BidSz , "BidSz", LVCFMT_RIGHT, 50, int, m_nBidSize), \
(COLHDR_DELTAS_COL_Sprd , "Sprd", LVCFMT_RIGHT, 50, double, m_dblSpread), \
(COLHDR_DELTAS_COL_Ask , "Ask", LVCFMT_RIGHT, 50, double, m_dblAsk), \
(COLHDR_DELTAS_COL_AskSz , "AskSz", LVCFMT_RIGHT, 50, int, m_nAskSize), \
(COLHDR_DELTAS_COL_Pos , "Pos", LVCFMT_RIGHT, 50, int, m_nPosition), \
(COLHDR_DELTAS_COL_AvgCst, "AvgCst", LVCFMT_RIGHT, 50, double, m_dblAverageCost), \
(COLHDR_DELTAS_COL_Delta , "Delta", LVCFMT_RIGHT, 50, double, m_dblDelta), \
(COLHDR_DELTAS_COL_Gamma , "Gamma", LVCFMT_RIGHT, 50, double, m_dblGamma), \
(COLHDR_DELTAS_COL_UnRlPL, "UnRlPL", LVCFMT_RIGHT, 50, double, m_dblUnrealizedPL), \
(COLHDR_DELTAS_COL_RlPL , "RlPL", LVCFMT_RIGHT, 50, double, m_dblRealizedPL) \
) \
) \
/**/
#define COLHDR_DELTAS_EXTRACT_COL_DETAILS(z, n, m, text) \
BOOST_PP_TUPLE_ELEM( \
COLHDR_DELTAS_ARRAY_ELEMENT_SIZE, m, \
BOOST_PP_ARRAY_ELEM( n, COLHDR_DELTAS_ARRAY ) \
)
#define COLHDR_DELTAS_EXTRACT_ENUM_LIST(z, n, text) \
BOOST_PP_COMMA_IF(n) \
COLHDR_DELTAS_EXTRACT_COL_DETAILS( z, n, 0, text )
#define COLHDR_DELTAS_EMIT_InsertColumn( z, n, VAR ) \
m_vuDeltas.InsertColumn( VAR++, \
_T(COLHDR_DELTAS_EXTRACT_COL_DETAILS(z, n, 1, ~)), \
COLHDR_DELTAS_EXTRACT_COL_DETAILS(z, n, 2, ~), \
COLHDR_DELTAS_EXTRACT_COL_DETAILS(z, n, 3, ~) \
);
#define COLHDR_DELTAS_EMIT_DefineVars( z, n, text ) \
COLHDR_DELTAS_EXTRACT_COL_DETAILS(z, n, 4, ~) \
COLHDR_DELTAS_EXTRACT_COL_DETAILS(z, n, 5, ~)\
;
Then in my class declaration, I can extract the enumerations in the correct 0-based order:
enum enumColHdrDeltasCol {
BOOST_PP_REPEAT( BOOST_PP_ARRAY_SIZE( COLHDR_DELTAS_ARRAY ), COLHDR_DELTAS_EXTRACT_ENUM_LIST, ~ )
};
The repetitive code of creating the columns in the CListView is handled through repetition and
extraction macros:
int ix = 0;
BOOST_PP_REPEAT( BOOST_PP_ARRAY_SIZE( COLHDR_DELTAS_ARRAY ), COLHDR_DELTAS_EMIT_InsertColumn, ix )
// m_vuDeltas.InsertColumn( ix++, "UndSym", LVCFMT_LEFT, 50 );
I'll be able to further use the initial structure to create the row factory for keeping the CListView
and row-structures synchronized. If I happen to change my mind on column ordering, all related code sections are
automatically updated.
[/Personal/SoftwareDevelopment/CPP]
permanent link
|