Defekt eigenes (Spaß) Präprozessors.
-
Ich habe zum verstehen aller Regeln des Präprozessors einen eigenen geschrieben. Ich scheine noch nicht alle Regeln der Makroexpansion erfasst zu haben. Es fehlt bei einem speziellen Fall ein letzter Scan.
Deshalb wollte ich hier nach den Regeln der Makroexpansion nachfragen.Ich habe mir das hier durchgelesen:
https://www.mirbsd.org/htman/i386/manINFO/cppinternals.html#Topund das hier mehrmals:
https://gcc.gnu.org/onlinedocs/cpp/Hier ein paar Auszüge aus meinen Unit Tests:
(Korrekte Fälle)
####################################################################################define x (4 + y) #define y (2 * x) x y
wird zu:
(4 + (2 * x)) (2 * (4 + y))
###################################################################################
#define A_1 b #define B(param) param ## 1 B(A_)
wird zu
b
###################################################################################
#include <boost/preprocessor/control/if.hpp> BOOST_PP_IF(10, a, b) // expands to a BOOST_PP_IF(0, a, b) // expands to b
wird zu
a b
###################################################################################
#define EMPTY() #define DEFER(id) id EMPTY() #define OBSTRUCT(id) id DEFER(EMPTY)() #define EXPAND(...) __VA_ARGS__ #define A() 123 A() // Expands to 123 DEFER(A)() // Expands to A () because it requires one more scan to fully expand EXPAND(DEFER(A)()) // Expands to 123, because the EXPAND macro forces another scan
wird zu:
123 A() 123
###################################################################################
Problemfälle
####################################################################################include <boost/preprocessor/control/if.hpp> #include <boost/preprocessor/facilities/empty.hpp> #include <boost/preprocessor/punctuation/comma.hpp> BOOST_PP_IF(1, BOOST_PP_COMMA, BOOST_PP_EMPTY)() // expands to ,
wird zu:
BOOST_PP_COMMA()
###################################################################################
#include <boost/preprocessor/repetition/enum.hpp> #define TEXT(z, n, text) text BOOST_PP_ENUM(4, TEXT, class) // expands to class, class, class, class
wird zu:
BOOST_PP_ENUM_1(4, TEXT, class)
###################################################################################
Der bleibt bei sowas immer kleben.
Ich vermute, dass man mir hier so nicht antworten werden können wird, deshalb versuche ich zuletzt noch den (momentan noch sehr langsamen - Ich will es noch beschleunigen optimieren wenn ich fertig bin) zugrunde liegenden Code zu kürzen:
// [...] bedeutet dass ich code entfernt habe
void Preprocessor::expandTokensFully(token_iterator line, std::vector <std::unique_ptr <Tokenizer::Token>>& tokens, std::vector <std::string> alreadyExpanded) { std::vector <std::unique_ptr <Tokenizer::Token>> newTokens; // this will hold all results bool stringizeNext = false; // [...] some self explanatory lambdas here for (; i != std::end(tokens); ++i) { // token pasting / token concatenation if (test_next("##", i)) { // [...] does some token pasting here } if ((*i)->toString() == "#") { ERROR_ADD(StrayPPOP, LINE_NUMBER, {"#"}); } if ((*i)->getID() == Tokenizer::TokenID::IDENTIFIER) { // is identifier a macro? auto def = pImpl->definitions_.find((*i)->toString()); if (def == pImpl->definitions_.end()) { // the identifier is not a macro, so just add the identifier to the token list addToken(i); // is a lambda - adds token to "newTokens" continue; } } // self referential macro breaker //#################################################################################### if (std::find(std::begin(alreadyExpanded), std::end(alreadyExpanded), def->first) != std::end(alreadyExpanded)) { addToken(i); continue; } //#################################################################################### // amount of expected parameters: auto expectedCount = def->second.parameters.size(); // if expectedCount > 0 -> functionMacro if (def->second.hasArgumentList) { if (!test_next("(", i)) { // is not this macro! -> therefor just an ordinary identifier addToken(i); } else { ++i; // get macro arguments std::vector <std::vector <std::unique_ptr<Tokenizer::Token>>> actualParameters; gatherArguments(i, std::end(tokens), actualParameters, line); // [...] Error Handling Here if (!def->second.parameters.empty()) { // do argument prescan argumentPrescan (actualParameters, line); // [...] Error Handling Here } // DO EXPANSION std::vector <std::unique_ptr <Tokenizer::Token> > temporaryBranch; expand(line, def->second, actualParameters, stringizeNext, newTokens, temporaryBranch); // I suspect this part to be very slow, but it was the "easiest" solution I could come up with for now auto alt_state = alreadyExpanded; alreadyExpanded.push_back(def->first); expandTokensFully(temporaryBranch, line, alreadyExpanded); // recursive call creates branches that kill self referential macros moveTokensOver(newTokens, temporaryBranch); alreadyExpanded = std::move(alt_state); } } else // no parameters expected { // [...] just copies tokens } } // end token loop tokens = std::move(newTokens); }
Ich komme nicht drauf. Es sind immer diese Fälle in denen durch die Ersetzung ein Makroname entsteht und eine Argumentliste folgt. Aber wenn ich weitere scans hinzufüge zerstöre ich immer die Regelung für selbstreferenzierenden Makros oder das Beispiel mit DEFER.
EDIT: expandTokensFully wird immer auf "eine Zeile" voller Tokens angewendet.
-
Wie lang wäre denn ein vollständiges Beispiel? Das Problem klingt interessant, aber sich durch den auch noch gekürzten Teil eines Abschnitts eines fremden Programms zu denken ist immer sehr schwer.
PS: Folgendes Dokument kennst du?
https://gcc.gnu.org/onlinedocs/cppinternals/
-
Ich habs in ein Git gepushed.
Ich erwarte nicht, dass da jemand raufschaut, aber Fragen kostet ja nichts.https://bitbucket.org/5cript/preprocessor/src
getestet mit MinGW (g++ 4.9 aber 4.8 müsste auch reichen) und Visual Studio.
Abhängigkeiten:
- boost system
- boost filesystem
- boost regexder gepostete Ausschnitt stammt aus "Preprocessor.cpp"
Der Code enthielt "zum Glück" schon Kommentare (könnte aber vielleicht ein paar mehr vertragen :/). Außerdem sind die Javadoc like Kommentare unvollständig -.-
Zuletzt: Ich gehe hier und da "den einfachen Weg". Trotzdem nehme ich gerne Kritik am Code entgegen. Vielleicht kommt da ja etwas wozu ich nicht einfach sagen würde: "war ich zu faul zu"
PS: Schon witzig, was Visual Studio alles kompiliert...
EDIT: Kann es sein, dass VS die tabs nicht in Leerzeichen umwandelt? *seufz*