#! /bin/bash # (C) Copyright Wolf Lammen 2010 # Distributed under the Boost Software License, Version 1.0. (See # copy at http://www.boost.org/LICENSE_1_0.txt) # this file runs a test suite checking the validity of a BOOST_PP_SEQ_REPLACE # implementation. It uses a brute force method, i.e. sequences of all # possible lengths are tested, each element is replaced, and each # replacement is individually checked. # This is done for both 1.43.0 code and the new experimental implementation. # In addition, this suite allows testing sequences with elements having a # critical pattern, i.e. containing commas, parentheses and other characters # special to the preprocessor. # The suite has been augmented to even tell if the (irrelevant) distribution of # space characters between elements differs in old and new code # # Tests are done by generating a C++ file, that is compiled and run then. The output # of compiled program is both logged to a file, and displayed on the terminal. # script parameters # fill in appropriate values here # temporary files, build files and result files are created here testPath="$HOME/testseq" # pattern from which elements in the sequence are built # a single occurrence of %s is replaced by the element index, # starting with 1 # format patterns must not exceed 100 characters #eFormat='(%s)' # (1)(2)(3)... #eFormat='(%s) ' # (1) (2) (3)... #eFormat='((%s)) ' # ((1)) ((2)) ((3))... #eFormat='((%s,))' # ((1,))((2,))((3,))... #eFormat='((,))' # ((,))((,))((,))... eFormat='(() %s)' # (() 1)(() 2)(() 3)... # a replacement is made using the following contents. Omit the enclosing # parentheses eReplacement='x' # start with sequences of size (inclusive, > 0)... szBegin=1 # ...and stop with sequences of size (inclusive, <= BOOST_PP_LIMIT_SEQ) szEnd=256 # pre-defined compiler configuration according to boost/preprocessor/config/config.hpp # if no boostFlags variable is defined, or if it is empty, the flags are automatically # determined according to the used compiler boostFlags='' #boostFlags='(BOOST_PP_CONFIG_EDG() | BOOST_PP_CONFIG_STRICT())' #boostFlags='0x0003' # invoking compiler boostIncludePath="$HOME/programs/boost_1_43_0" cppFile="$testPath/testseq.cpp" exeFile='testseq' cc="g++ -Wall -I$boostIncludePath -o $exeFile $cppFile" #tests LF=' ' cd "$testPath" for seqsz in `seq -s ' ' $szBegin $szEnd` do # create C++ source file cat > "$cppFile" << END /* automatically generated file used to test replacement in BOOST sequences. The size of the sequence tested is $seqsz. Large parts of this file are verbatim copies from a file subject to (C) Copyright Wolf Lammen 2010 */ END if [ "${boostFlags:-x}" != 'x' ] then echo "#define BOOST_PP_CONFIG_FLAGS() $boostFlags" >> "$cppFile" fi cat >> "$cppFile" << END // includes needed for test code # include # include # include # include # include "boost/preprocessor/arithmetic/dec.hpp" # include "boost/preprocessor/stringize.hpp" // includes needed for experimental PP_SEQ_REPLACE # include "boost/preprocessor/arithmetic/inc.hpp" # include "boost/preprocessor/config/config.hpp" # include "boost/preprocessor/facilities/empty.hpp" # include "boost/preprocessor/seq/first_n.hpp" # include "boost/preprocessor/seq/detail/split.hpp" # include "boost/preprocessor/tuple/elem.hpp" // includes needed for old BOOST_PP_SEQ_REPLACE # include "boost/preprocessor/seq/replace.hpp" using std::string; using std::cout; using std::endl; // experimental PP_SEQ_REPLACE implementation // to avoid name clashes, the BOOST_ prefix is omitted for macros defined here # if ~BOOST_PP_CONFIG_FLAGS() & BOOST_PP_CONFIG_EDG() # define PP_SEQ_REPLACE(seq, i, elem) BOOST_PP_SEQ_FIRST_N(i, seq) (elem) PP_SEQ_REST_N_PLUS_1(i, seq) # define PP_SEQ_REST_N_PLUS_1(n, seq) BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_SEQ_SPLIT(BOOST_PP_INC(n), seq BOOST_PP_EMPTY))() # else # define PP_SEQ_REPLACE(seq, i, elem) PP_SEQ_REPLACE_I(seq, i, elem) # define PP_SEQ_REPLACE_I(seq, i, elem) BOOST_PP_SEQ_FIRST_N(i, seq) (elem) PP_SEQ_REST_N_PLUS_1(i, seq) # define PP_SEQ_REST_N_PLUS_1(n, seq) PP_SEQ_REST_N_PLUS_1_I(n, seq) # define PP_SEQ_REST_N_PLUS_1_I(n, seq) BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_SEQ_SPLIT(BOOST_PP_INC(n), seq BOOST_PP_EMPTY))() # endif # define REPLACE $eReplacement END seqlist=`seq -s ' ' 1 $seqsz` # create the sequence from pattern echo '#define SEQ \' >> "$cppFile" for i in $seqlist do printf "$eFormat\\$LF" $i >> "$cppFile" done echo >> "$cppFile" # make pattern and replacement available to the program cat >> "$cppFile" << END char const elemPattern[] = "$eFormat"; char const replacement[] = "$eReplacement"; END # create macro expansions with boost 1.43.0 code echo "char const* const oldResult[$seqsz] = {" >> "$cppFile" for i in $seqlist do echo "BOOST_PP_STRINGIZE(BOOST_PP_SEQ_REPLACE(SEQ, BOOST_PP_DEC($i), REPLACE))," >> "$cppFile" done echo '};' >> "$cppFile" # create macro expansions with new code echo "char const* const newResult[$seqsz] = {" >> "$cppFile" for i in $seqlist do echo "BOOST_PP_STRINGIZE(PP_SEQ_REPLACE(SEQ, BOOST_PP_DEC($i), $eReplacement))," >> "$cppFile" done echo '};' >> "$cppFile" echo >> "$cppFile" # utility functions for runtime check cat >> "$cppFile" << END void trimLeft(char const** str) { // advances *str by the number of leading spaces while(**str == ' ') ++*str; } string trim(string const& s) { // removes leading and trailing spaces unsigned low = s.find_first_not_of(" "); unsigned high = s.find_last_not_of(" "); return s.substr(low, high + 1 - low); } string elemFromPattern(unsigned i) { // returns the i-th element (zero-based), trimmed, according to eFormat static string pattern = trim(elemPattern); char buffer_i[10]; char buffer[115]; snprintf(buffer_i, sizeof(buffer_i), "%u", i + 1); snprintf(buffer, sizeof(buffer), pattern.c_str(), buffer_i); return buffer; } bool cmpElem(char const** expansion, string const& e) { // checks whether the beginning of *expansion matches e // if so advances *expansion, so that e as well as following spaces // are skipped unsigned sz = e.size(); bool result = e.compare(0, sz, *expansion, sz) == 0; if (result) { *expansion += sz; trimLeft(expansion); } return result; } bool cmpOrigElem(char const** expansion, unsigned idx) { // checks whether the beginning of *expansion matches the element // in the original sequence at position 'idx' (zero-based) // if so advances *expansion, so that the first element as well as // following spaces are skipped return cmpElem(expansion, elemFromPattern(idx)); } bool cmpReplacement(char const** expansion) { // checks whether the beginning of *expansion matches a replaced // element // if so advances *expansion, so that the first element as well as // following spaces are skipped return cmpElem(expansion, '(' + string(replacement) + ')'); } int check(char const* expansion, unsigned replacepos) { // checks whether 'expansion' is a sequense equal to the original sequence, // except for position 'replacepos' (zero-based) which must be the // replaced element trimLeft(&expansion); bool ok = true; unsigned i = 0; for (; ok && ++i <= $seqsz;) ok = i - 1 == replacepos? cmpReplacement(&expansion) : cmpOrigElem(&expansion, i - 1); return ok && *expansion == '\0'? 0 : i; } int main() { cout << "testing sequence of length " << $seqsz << endl; for(unsigned i = 0; i < $seqsz; ++i) { // check replacement at position #i unsigned idx1 = check(oldResult[i], i); if (idx1 != 0) cout << "boost 1.43.0 code failed when replacing element #" << i + 1 << endl << "element #" << idx1 << " is not as expected" << endl << "expansion is :" << endl << oldResult[i] << endl; unsigned idx2 = check(newResult[i], i); if (idx2 != 0) cout << "experimental code failed when replacing element #" << i + 1 << endl << "element #" << idx2 << " is not as expected" << endl << "expansion is :" << endl << newResult[i] << endl; if (idx1 == 0 && idx2 == 0 && strcmp(oldResult[i], newResult[i]) != 0) cout << "experimental code is correct, when replacing element #" << i + 1 << endl << "but white space distribution differs from 1.43.0 result" << endl; } } END if $cc then ./$exeFile | tee testresults.log else break fi done