home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / mfc / utility / templdef / templdef.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-03-27  |  15.7 KB  |  703 lines

  1. // templdef.cpp : A simple C++ template expansion utility.
  2. //
  3. //   This utility takes a template file which uses a syntax
  4. //   subset similar to the proposed C++ template syntax
  5. //   and expands it into the equivalent non-template C++ syntax.
  6. //
  7. //   Note that this is intended as an example program, and
  8. //   handles only a specific syntax in the template files.
  9. //
  10. // This is a part of the Microsoft Foundation Classes C++ library.
  11. // Copyright (C) 1992-1998 Microsoft Corporation
  12. // All rights reserved.
  13. //
  14. // This source code is only intended as a supplement to the
  15. // Microsoft Foundation Classes Reference and related
  16. // electronic documentation provided with the library.
  17. // See these sources for detailed information regarding the
  18. // Microsoft Foundation Classes product.
  19.  
  20. #include <ctype.h>
  21. #include <afxcoll.h>
  22. #include <afxtempl.h>
  23.  
  24. class CTest1 : public CTypedPtrList<CObList, CObList*>
  25. {
  26. };
  27.  
  28. #if 0
  29. class A
  30. {
  31. };
  32.  
  33. class B : public A
  34. {
  35. };
  36.  
  37. class AA
  38. {
  39. };
  40.  
  41. void Foo()
  42. {
  43.     AA* pv;
  44.     B* pb;
  45.     A* pa1 = &static_cast<A&>(*pb);
  46. //  A* pa2 = &static_cast<A&>(*pv);
  47.     B* pb2 = &static_cast<B&>(*pa1);
  48. }
  49.  
  50. class Y
  51. {
  52. };
  53.  
  54. class YList : public Y
  55. {
  56. };
  57.  
  58. class X
  59. {
  60. public:
  61.     void Foo(Y* y);
  62.     void Foo(YList* ylist);
  63. };
  64.  
  65. class Xprime : public X
  66. {
  67. private:
  68.     void Foo(YList* ylist);
  69. };
  70.  
  71. class Xprime1 : public Xprime
  72. {
  73. public:
  74.     void Foo(Y* y);
  75. };
  76.  
  77. void Bar()
  78. {
  79.     Xprime1* p;
  80.     YList* l;
  81.     p->Foo(l);
  82. }
  83. #endif
  84.  
  85. #define SIGNON_VER 3
  86. #define SIGNON_REV 0
  87.  
  88. #define istringTypeParamMax 10
  89.  
  90. CString strHFileName;
  91. CString strInlFileName;
  92. CString strCppFileName;
  93. CString strCttFileName;
  94.  
  95. CString stringTemplate = "template";
  96. CString stringClass = "class";
  97. CString stringTemplateName;
  98. CString stringTypedefName;
  99.  
  100. const char chMoreThanOne = '\0';
  101. const char chNot = '\0';
  102.  
  103. /////////////////////////////////////////////////////////////////////////////
  104.  
  105. void Error(const char* szErrorMessage, const char* szErrorParam = NULL)
  106. {
  107.     fprintf(stderr, "templdef : error: ");
  108.     fprintf(stderr, szErrorMessage, szErrorParam);
  109.     fprintf(stderr, ".\n");
  110.     exit(1);
  111. }
  112.  
  113. void PrintUsage()
  114. {
  115.     fprintf(stderr, "templdef usage (one line):\n\n"
  116.         "  templdef [/Dsymbol]... \"CTemplateName<arg1, arg2> CClassName\"\n"
  117.         "\tinfile.ctt outfile.h outfile.inl outfile.cpp\n"
  118.         "\n"
  119.         "  Multiple /D switches may be used to define symbols processed by $ifdef.\n"
  120.         "\n"
  121.         "  This command expands a template class to be equivalent to:\n"
  122.         "\n"
  123.         "\ttypedef CTemplateName<arg1, arg2> CClassName;\n");
  124. }
  125.  
  126. void CheckSyntax(BOOL fGoodSyntax, const char* sz)
  127. {
  128.     if (!fGoodSyntax)
  129.         Error("expecting %s", sz);
  130. }
  131.  
  132. /////////////////////////////////////////////////////////////////////////////
  133. // CTokenFile
  134. // For the purpose of reading source code, this sort of CStdioFile derivative
  135. // proves quite handy.  This could be made really powerful, but this is a
  136. // start.
  137.  
  138. class CTokenFile : public CStdioFile
  139. {
  140. private:
  141.     enum { ichMax = 1024 };
  142.     char ach[ichMax];
  143.     int ich;
  144.     static CString stringToken;
  145.     BOOL fComment;
  146.     void GetBuf();
  147.     char GetChar() { if (ach[ich] == chNot) GetBuf(); return ach[ich++]; }
  148.     void UnGetChar() { --ich; } // note: really a single char pushback
  149. public:
  150.     char PeekChar() { return ach[ich]; }
  151.     void InitBuf(const char* sz);
  152.     static CString& TokenString() { return stringToken; }
  153.     CTokenFile();
  154.     char GetToken();
  155.     char GetPrintToken();
  156.     char GetTypeToken();
  157.     char GetNameToken();
  158.     void PutToken(char ch) { fputc(ch, m_pStream); }
  159.     void PutToken(const CString& string) { WriteString(string); }
  160.     void PutToken() { WriteString(stringToken); }
  161.     BOOL AtFileEnd() { return ach[0] == chNot; }
  162.     BOOL AtBufEnd() { return ach[ich] == chNot; }
  163.     void SafeOpen(const CString& string, UINT nStyleFlags);
  164. };
  165. CString CTokenFile::stringToken;
  166.  
  167. CTokenFile::CTokenFile()
  168. {
  169.     ach[0] = '\n';
  170.     ach[1] = chNot;
  171.     ich = 1;
  172.     fComment = FALSE;
  173. };
  174.  
  175. void CTokenFile::GetBuf()
  176. {
  177.     if (ReadString(ach, ichMax) == NULL)
  178.         ach[0] = chNot;
  179.  
  180.     ich = 0;
  181. }
  182.  
  183. void CTokenFile::InitBuf(const char* sz)
  184. {
  185.     strncpy(ach, sz, ichMax);
  186.     ich = 0;
  187. }
  188.  
  189. void CTokenFile::SafeOpen(const CString& string, UINT nStyleFlags)
  190. {
  191.     BOOL fSuccess = Open(string, nStyleFlags, 0);
  192.  
  193.     if (!fSuccess)
  194.         Error("can't open file \"%s\"", string);
  195. }
  196.  
  197. char CTokenFile::GetToken()
  198. {
  199.     if (AtBufEnd())
  200.         GetBuf();
  201.  
  202.     if (AtFileEnd())
  203.         exit(0);
  204.  
  205.     fComment = FALSE;
  206.  
  207.     char ch = GetChar();
  208.     char* pch = stringToken.GetBuffer(1024);
  209.     char* pchInit = pch;
  210.     *pch++ = ch;
  211.     *pch = '\0';
  212.  
  213.     // assuming this doesn't "really" release the buffer!
  214.     stringToken.ReleaseBuffer(1);
  215.  
  216.     if (!isalnum(ch) &&
  217.         ch != '/'    &&
  218.         ch != '_'    &&
  219.         ch != '\''   &&
  220.         ch != '"'    &&
  221.         ch != '#'    &&
  222.         ch != '$')
  223.     {
  224.         return ch;
  225.     }
  226.  
  227.     if ((ch == '\'') || (ch == '"'))
  228.     {
  229.         char ch2;
  230.         while ((ch2 = GetChar()) != ch)
  231.         {
  232.             *pch++ = ch2;
  233.         }
  234.         *pch++ = ch2;
  235.         stringToken.ReleaseBuffer(pch - pchInit);
  236.         return chMoreThanOne;
  237.     }
  238.  
  239.     if (ch == '/')
  240.     {
  241.         char ch2 = GetChar();
  242.         if (AtFileEnd()) exit(0);
  243.         if (ch2 == '/')
  244.         {
  245.             char ch3 = GetChar();
  246.             if (ch3 != '$')
  247.             {
  248.                 UnGetChar();
  249.                 *pch++ = '/';
  250.                 fComment = TRUE;
  251.                 while ((ch3 = GetChar()) != '\n')
  252.                     *pch++ = ch3;
  253.             }
  254.             else
  255.             {
  256.                 stringToken.ReleaseBuffer(0);
  257.             }
  258.             *pch++ = '\n';
  259.             stringToken.ReleaseBuffer(pch - pchInit);
  260.             return chMoreThanOne;
  261.         }
  262.         else if (ch2 == '*')
  263.         {
  264.             char ch3 = chNot;
  265.             char ch4 = chNot;
  266.             *pch++ = '*';
  267.             fComment = TRUE;
  268.             while (ch4 != '/')
  269.             {
  270.                 while ((ch3 = GetChar()) != '*')
  271.                     *pch++ = ch3;
  272.                 *pch++ = '*';
  273.  
  274.                 ch4 = GetChar();
  275.                 if (ch4 != '/')
  276.                     UnGetChar();
  277.             }
  278.             *pch++ = '/';
  279.             stringToken.ReleaseBuffer(pch - pchInit);
  280.             return chMoreThanOne;
  281.         }
  282.         else
  283.         {
  284.             UnGetChar();
  285.             return ch;
  286.         }
  287.     }
  288.  
  289.     if (isdigit(ch))
  290.     {
  291.         char ch2;
  292.         ch2 = GetChar();
  293.         if (!isalpha(ch2))
  294.         {
  295.             UnGetChar();
  296.             return ch;
  297.         }
  298.  
  299.         *pch++ = ch2;
  300.         stringToken.ReleaseBuffer(pch - pchInit);
  301.         return chMoreThanOne;
  302.     }
  303.  
  304.     while ((!AtFileEnd()) &&
  305.         (ch = GetChar(), (isalnum(ch) || (ch == '_'))))
  306.     {
  307.         *pch++ = ch;
  308.     }
  309.  
  310.     if (!AtFileEnd())
  311.         UnGetChar();
  312.  
  313.     stringToken.ReleaseBuffer(pch - pchInit);
  314.     return chMoreThanOne;
  315. }
  316.  
  317. char CTokenFile::GetPrintToken()
  318. {
  319.     char chToken;
  320.     while (((chToken = GetToken()) != chMoreThanOne) &&
  321.            (isspace(chToken)) || fComment)
  322.         /* try again */ ;
  323.  
  324.     return chToken;
  325. }
  326.  
  327. char CTokenFile::GetTypeToken()
  328. {
  329.     char chToken = GetPrintToken();
  330.     CString typeStr = stringToken;
  331.     char chFirst = typeStr[0];
  332.  
  333.     CheckSyntax(
  334.         isalnum(chFirst) ||
  335.         chFirst == '_'   ||
  336.         chFirst == '\''  ||
  337.         chFirst == '"',
  338.         "a type description or constant starting with an\n"
  339.         "alphanumeric or an underbar, or a string or char constant");
  340.  
  341.     while ((chToken = GetPrintToken()) != ',' && chToken != '>')
  342.     {
  343.         if (chToken != '*' && chToken != '&' && !isdigit(chToken))
  344.             typeStr += ' ';
  345.  
  346.         typeStr += stringToken;
  347.     }
  348.  
  349.     stringToken = typeStr;
  350.     return chToken;
  351. }
  352.  
  353. char CTokenFile::GetNameToken()
  354. {
  355.     GetPrintToken();
  356.  
  357.     CheckSyntax((isalpha(stringToken[0]) || stringToken[0] == '_'),
  358.         "a name token starting in an alpha char or underbar");
  359.  
  360.     int l = stringToken.GetLength();
  361.     for (int i=1; i<l; ++i)
  362.     {
  363.         CheckSyntax(isalnum(stringToken[i]) || stringToken[i] == '_',
  364.             "a name token consisting of alphanumerics or underbars");
  365.     }
  366.  
  367.     CheckSyntax(stringToken != stringTemplate, "a name token");
  368.     CheckSyntax(stringToken != stringClass, "a name token");
  369.  
  370.     return chMoreThanOne;
  371. }
  372.  
  373. /////////////////////////////////////////////////////////////////////////////
  374.  
  375. CMapStringToString map;
  376.  
  377. BOOL isTailMatch(CString string, CString tail)
  378. {
  379.     BOOL retval = string.Right(tail.GetLength()) == tail;
  380.     ASSERT(retval);
  381.     return retval;
  382. }
  383.  
  384. CTokenFile fileH;
  385. CTokenFile fileInl;
  386. CTokenFile fileCpp;
  387. CTokenFile fileCtt;
  388.  
  389. struct ConditionalState
  390. {
  391.     ConditionalState()
  392.         { memset(this, 0, sizeof *this); }
  393.  
  394.     BOOL fSwallow;
  395.     BOOL fSwallowOld;
  396.     BOOL finIf;
  397.     BOOL finElse;
  398.     BOOL fSwallowIf;
  399.     BOOL fSwallowElse;
  400. };
  401.  
  402. struct StateStack
  403. {
  404.     void PushState(ConditionalState& state)
  405.         { m_stack.AddHead(state); }
  406.  
  407.     void PopState(ConditionalState& state)
  408.         { state = m_stack.RemoveHead(); }
  409.  
  410.     CList<ConditionalState, const ConditionalState&> m_stack;
  411. };
  412.  
  413. // TranslateTo:
  414. // Copy fileCtt to fileOut, translating words as necessary.
  415. // On the way, swallow things outside of blocks and after
  416. // the "template" word inside of '<' '>' brackets.
  417. // Return if "//$" comment matches pszEnd string
  418. //
  419. // Also, on the way, any $ifdef or $else statements will be
  420. // swallowed as appropriate.
  421. //
  422. void TranslateTo(CTokenFile& fileOut, const char* pszEnd)
  423. {
  424.     char chToken;
  425.     static BOOL fSwallowParams = TRUE;
  426.     ConditionalState state;
  427.     StateStack stack;
  428.     CString stringTokenOut;
  429.  
  430.     while ((chToken = fileCtt.GetToken()) != chMoreThanOne ||
  431.             (!fileCtt.TokenString().IsEmpty() && fileCtt.TokenString() != pszEnd))
  432.     {
  433.         if (chToken != chMoreThanOne)
  434.         {
  435.             if (chToken == '{')         /* match } */
  436.                 fSwallowParams = FALSE;
  437.             if (fSwallowParams && (chToken == '<'))
  438.             {
  439.                 while (chToken != '>')
  440.                     chToken = fileCtt.GetToken();
  441.                 // if template is at end of line eat end of line as well
  442.                 if (fileCtt.PeekChar() == '\n')
  443.                     fileCtt.GetToken(); // skip the '\n'
  444.             }
  445.             else
  446.             {
  447.                 if (!state.fSwallow)
  448.                     fileOut.PutToken();
  449.             }
  450.         }
  451.         else
  452.         {
  453.             if (fileCtt.TokenString()[0] == '$')
  454.             {
  455.                 if (fileCtt.TokenString() == "$ifdef")
  456.                 {
  457.                     stack.PushState(state);
  458.                     fileCtt.GetPrintToken();
  459.                     if (map.Lookup(fileCtt.TokenString(), stringTokenOut))
  460.                     {
  461.                         state.fSwallowElse = TRUE;
  462.                         state.fSwallowIf = FALSE;
  463.                     }
  464.                     else
  465.                     {
  466.                         state.fSwallowElse = FALSE;
  467.                         state.fSwallowIf = TRUE;
  468.                     }
  469.  
  470.                     if (state.fSwallowIf || state.fSwallowElse)
  471.                     {
  472.                         fileCtt.TokenString() = "";
  473.                         state.finIf = TRUE;
  474.                     }
  475.                     state.fSwallowOld = state.fSwallow;
  476.                     state.fSwallow = state.fSwallowIf || state.fSwallowOld;
  477.                 }
  478.                 else if (fileCtt.TokenString() == "$ifndef")
  479.                 {
  480.                     stack.PushState(state);
  481.                     fileCtt.GetPrintToken();
  482.                     if (!map.Lookup(fileCtt.TokenString(), stringTokenOut))
  483.                     {
  484.                         state.fSwallowElse = TRUE;
  485.                         state.fSwallowIf = FALSE;
  486.                     }
  487.                     else
  488.                     {
  489.                         state.fSwallowElse = FALSE;
  490.                         state.fSwallowIf = TRUE;
  491.                     }
  492.  
  493.                     if (state.fSwallowIf || state.fSwallowElse)
  494.                     {
  495.                         fileCtt.TokenString() = "";
  496.                         state.finIf = TRUE;
  497.                     }
  498.                     state.fSwallowOld = state.fSwallow;
  499.                     state.fSwallow = state.fSwallowIf || state.fSwallowOld;
  500.                 }
  501.                 else if (fileCtt.TokenString() == "$else")
  502.                 {
  503.                     if (state.finIf)
  504.                     {
  505.                         fileCtt.TokenString() = "";
  506.                         state.finIf = FALSE;
  507.                         state.finElse = TRUE;
  508.                     }
  509.                     state.fSwallow = state.fSwallowElse || state.fSwallowOld;
  510.                 }
  511.                 else if (fileCtt.TokenString() == "$endif")
  512.                 {
  513.                     if (state.finIf || state.finElse)
  514.                     {
  515.                         fileCtt.TokenString() = "";
  516.  
  517.                         // eat any evil junk after the $endif
  518.                         while ((chToken = fileCtt.GetToken()) != '\n' &&
  519.                             fileCtt.TokenString()[0] != '/')
  520.                             /* spin */ ;
  521.  
  522.                         if (chToken != '\n')
  523.                             fileCtt.GetToken();
  524.                     }
  525.                     stack.PopState(state);
  526.                 }
  527.             }
  528.  
  529.             if (fileCtt.TokenString() == stringTemplate)
  530.                 fSwallowParams = TRUE;
  531.  
  532.             if (map.Lookup(fileCtt.TokenString(), stringTokenOut))
  533.             {
  534.                 if (!state.fSwallow)
  535.                     fileOut.PutToken(stringTokenOut);
  536.             }
  537.             else
  538.             {
  539.                 if (!state.fSwallow)
  540.                     fileOut.PutToken();
  541.             }
  542.         }
  543.     }
  544. }
  545.  
  546. // main:
  547. // Gets the arguments, checks them, then processes the files.
  548. //
  549. main(int argc, char* argv[])
  550. {
  551.     fprintf(stderr,
  552.         "\nMicrosoft (R) C++ Template Definition Utility   Version %d.%02d\n"
  553.         "Copyright (c) Microsoft Corp. 1992-1998. All rights reserved.\n\n",
  554.         SIGNON_VER, SIGNON_REV);
  555.  
  556.     // Process any switches
  557.     //
  558.     for (int i=1; i<(argc-3) && argv[i][0] == '/'; ++i)
  559.     {
  560.         switch (argv[i][1])
  561.         {
  562.         case 'D':
  563.             if (argv[i][2] != '\0')
  564.                 map.SetAt(&argv[i][2], "1");    // define the symbol as a 1
  565.             break;
  566.         default:
  567.             PrintUsage();
  568.             exit(1);
  569.         }
  570.     }
  571.  
  572.     // Process template creation arguments
  573.     //
  574.     CString stringTypes;
  575.     for (/*null*/; i<(argc-3); ++i)
  576.         stringTypes += CString(argv[i]) + ' ';
  577.  
  578.     if (argc < 5)
  579.     {
  580.         PrintUsage();
  581.         exit(1);
  582.     }
  583.  
  584.     // Copy the template, header and source file name arguments.
  585.     //
  586.     strCttFileName = argv[argc-4];
  587.     strHFileName   = argv[argc-3];
  588.     strInlFileName = argv[argc-2];
  589.     strCppFileName = argv[argc-1];
  590.  
  591.     // Check to make sure that the args are in the right order.
  592.     if (!isTailMatch(strCttFileName, ".ctt"))
  593.         Error("template file should have a .ctt extension");
  594.  
  595.     if (!isTailMatch(strCppFileName, ".cpp") &&
  596.         !isTailMatch(strCppFileName, ".cxx"))
  597.         Error("module file should have a .cpp or .cxx extension");
  598.  
  599.     if (!isTailMatch(strInlFileName, ".inl"))
  600.         Error("inline file should have an .inl extension");
  601.  
  602.     if (!isTailMatch(strHFileName, ".h") &&
  603.         !isTailMatch(strHFileName, ".hpp") &&
  604.         !isTailMatch(strHFileName, ".hxx"))
  605.         Error("header file should have an .h, .hpp or .hxx extension");
  606.  
  607.     // Open the files.
  608.     //
  609.     fileCtt.SafeOpen(strCttFileName, CTokenFile::modeRead);
  610.     fileH.SafeOpen(strHFileName,
  611.         CTokenFile::modeWrite | CTokenFile::modeCreate);
  612.     fileInl.SafeOpen(strInlFileName,
  613.         CTokenFile::modeWrite | CTokenFile::modeCreate);
  614.     fileCpp.SafeOpen(strCppFileName,
  615.         CTokenFile::modeWrite | CTokenFile::modeCreate);
  616.  
  617.     // Push the command line onto the file buffer, so that we can parse it
  618.     // using our standard tool set.
  619.     //
  620.     fileCtt.InitBuf(stringTypes);
  621.  
  622.     int chToken;
  623.     CString astringTypeParam[istringTypeParamMax];
  624.     int istringTypeParam = 0;
  625.  
  626.     fileCtt.GetNameToken();
  627.     stringTemplateName = fileCtt.TokenString();
  628.  
  629.     chToken = fileCtt.GetPrintToken();
  630.     CheckSyntax(chToken == '<', "'<'");
  631.  
  632.     do
  633.     {
  634.         chToken = fileCtt.GetTypeToken();
  635.         astringTypeParam[istringTypeParam++] = fileCtt.TokenString();
  636.         CheckSyntax(istringTypeParam < istringTypeParamMax,
  637.             "fewer parameterized types (program limit)");
  638.     } while (chToken == ',');
  639.  
  640.     CheckSyntax(chToken == '>', "'>'");
  641.  
  642.     fileCtt.GetNameToken();
  643.     stringTypedefName = fileCtt.TokenString();
  644.  
  645.     map.SetAt(stringTemplate, "");
  646.     map.SetAt(stringTemplateName, stringTypedefName);
  647.  
  648.     // Done processing the command line part, now eat any initial comments
  649.     // appearing before the //$DECLARE_TEMPLATE flag.
  650.     //
  651.     while ((chToken = fileCtt.GetPrintToken()) != chMoreThanOne ||
  652.            fileCtt.TokenString() != "DECLARE_TEMPLATE")
  653.     {
  654.         /* spin */ ;
  655.     }
  656.  
  657.     while ((fileCtt.GetToken() != chMoreThanOne) ||
  658.            (fileCtt.TokenString() != stringTemplate))
  659.     {
  660.         fileH.PutToken();
  661.     }
  662.  
  663.     // Token must now be "template".
  664.  
  665.     // Eat opening '<'.
  666.     //
  667.     chToken = fileCtt.GetPrintToken();
  668.  
  669.     // Now get a list of type parameters.
  670.     //
  671.     int istringTypeParamMaxPrev = istringTypeParam;
  672.     istringTypeParam = 0;
  673.     while (chToken !='>')
  674.     {
  675.         CString stringParamName;
  676.  
  677.         // The parameter name is the last thing before a ',' or '>'.
  678.         //
  679.         while ((chToken = fileCtt.GetPrintToken()) != ',' && chToken != '>')
  680.         {
  681.             stringParamName = fileCtt.TokenString();
  682.         }
  683.  
  684.         map.SetAt(stringParamName, astringTypeParam[istringTypeParam++]);
  685.     }
  686.  
  687.     CheckSyntax(istringTypeParam == istringTypeParamMaxPrev,
  688.         "same number of template parameters");
  689.  
  690.     // Copy template to header file, translating words as necessary,
  691.     // terminating when the //$IMPLEMENT_TEMPLATE_INLINES flag is hit.
  692.     TranslateTo(fileH, "IMPLEMENT_TEMPLATE_INLINES");
  693.  
  694.     // Copy template to inline file, translating words as necessary,
  695.     // terminating when the //$IMPLEMENT_TEMPLATE flag is hit.
  696.     TranslateTo(fileInl, "IMPLEMENT_TEMPLATE");
  697.  
  698.     // Copy template to source file, translating words as necessary.
  699.     TranslateTo(fileCpp, "END_OF_FILE");
  700.  
  701.     return 0;
  702. }
  703.