Zaczęło się od pytania SO, ale zdałem sobie sprawę, że jest to dość niekonwencjonalne i oparte na rzeczywistym opisie na stronach internetowych, może być lepiej dostosowane do programistów.se, ponieważ pytanie ma wiele pojęć koncepcyjnych.
Uczyłem się języka LibTooling i jest to bardzo potężne narzędzie, które może w przyjazny sposób ujawnić cały „drobiazgowy” charakter kodu, to znaczy semantycznie , a nie zgadując. Jeśli clang może skompilować kod, to clang jest pewien semantyki każdego pojedynczego znaku w tym kodzie.
Pozwól mi teraz cofnąć się na chwilę.
Istnieje wiele praktycznych problemów, które pojawiają się, gdy ktoś angażuje się w metaprogramowanie szablonów C ++ (a zwłaszcza gdy wyrusza poza szablony na terytorium sprytnych, choć przerażających makr). Szczerze mówiąc, dla wielu programistów, w tym mnie, wiele zwykłych zastosowań szablonów jest również nieco przerażających.
Wydaje mi się, że dobrym przykładem byłyby łańcuchy czasu kompilacji . To pytanie ma już ponad rok, ale jasne jest, że C ++ w tej chwili nie ułatwia tego zwykłym śmiertelnikom. Chociaż patrzenie na te opcje nie wystarcza, aby wywołać u mnie mdłości, niemniej jednak nie jestem przekonany, czy jestem w stanie stworzyć magiczny, maksymalnie wydajny kod maszynowy, pasujący do każdej fantazyjnej aplikacji, którą mam dla mojego oprogramowania.
Spójrzmy prawdzie w oczy, ludzie, łańcuchy są dość proste i podstawowe. Niektórzy z nas chcą po prostu wygodnego sposobu na wyemitowanie kodu maszynowego, który ma pewne „ciągłe” ciągi znaków znacznie więcej niż dostajemy, gdy kodujemy go w prosty sposób. W naszym kodzie C ++.
Wpisz clang i LibTooling, który udostępnia abstrakcyjne drzewo składniowe (AST) kodu źródłowego i pozwala prostej niestandardowej aplikacji C ++ poprawnie i niezawodnie manipulować surowym kodem źródłowym (za pomocą Rewriter
) wraz z bogatym semantycznym obiektowym modelem zorientowanym na wszystko w AST. Obsługuje wiele rzeczy. Wie o rozszerzeniach makr i pozwala śledzić te łańcuchy. Tak, mówię o transformacji kodu źródłowego lub tłumaczeniu.
Moją podstawową tezą jest to, że clang pozwala nam teraz tworzyć pliki wykonywalne, które same mogą działać jako idealne niestandardowe etapy preprocesora dla naszego oprogramowania C ++, a my możemy zaimplementować te etapy metaprogramowania w C ++. Jesteśmy po prostu ograniczeni przez fakt, że ten etap musi pobierać dane wejściowe, które są poprawnym kodem C ++ i produkować jako wynik bardziej poprawny kod C ++. Plus wszelkie inne ograniczenia, które stosuje system kompilacji.
Dane wejściowe muszą być co najmniej bardzo zbliżone do poprawnego kodu C ++, ponieważ w końcu clang jest frontonem kompilatora, a my po prostu szukamy i jesteśmy kreatywni dzięki jego interfejsowi API. Nie wiem, czy istnieje jakikolwiek przepis umożliwiający zdefiniowanie nowej składni do użycia, ale najwyraźniej musimy opracować sposoby jej prawidłowej analizy i dodać ją do projektu clang, aby to zrobić. Oczekiwać więcej, to mieć coś w projekcie clang, który jest poza zakresem.
Żaden problem. Wyobrażam sobie, że niektóre funkcje makr bez obsługi poradzą sobie z tym zadaniem.
Innym sposobem spojrzenia na to, co opisuję, jest zaimplementowanie konstrukcji metaprogramowania przy użyciu środowiska wykonawczego C ++ poprzez manipulowanie AST naszego kodu źródłowego (dzięki clang i jego API) zamiast implementowania ich przy użyciu bardziej ograniczonych narzędzi dostępnych w samym języku. Ma to również wyraźne zalety w zakresie wydajności kompilacji (nagłówki z dużym szablonem spowalniają kompilację proporcjonalnie do tego, jak często ich używasz. Wiele skompilowanych rzeczy jest następnie starannie dopasowywanych i wyrzucanych przez linker).
Jest to jednak kosztem wprowadzenia dodatkowego kroku lub dwóch w procesie kompilacji, a także wymogu napisania nieco (co prawda) nieco bardziej szczegółowego oprogramowania (ale przynajmniej jest to proste środowisko uruchomieniowe C ++) jako część naszego narzędzia .
To nie jest cały obraz. Jestem pewien, że generowanie kodu jest o wiele większe niż w przypadku podstawowych funkcji językowych. W C ++ możesz napisać szablon lub makro lub szaloną kombinację obu, ale w narzędziu clang możesz modyfikować klasy i funkcje w KAŻDY sposób, który możesz osiągnąć w C ++, w czasie wykonywania , mając jednocześnie pełny dostęp do treści semantycznej, oprócz szablonu i makr oraz wszystkiego innego.
Zastanawiam się więc, dlaczego wszyscy już tego nie robią. Czy to dlatego, że ta funkcja clang jest tak nowa i nikt nie zna ogromnej hierarchii klas AST klanu? To nie może być to.
Być może po prostu trochę nie doceniam trudności, ale „manipulowanie ciągiem znaków w czasie kompilacji” za pomocą narzędzia clang jest prawie kryminalnie proste. Jest pełny, ale niesamowicie prosty. Wszystko, czego potrzeba, to kilka makropoleceń, które odwzorowują rzeczywiste std::string
operacje. Wtyczka clang implementuje to, pobierając wszystkie odpowiednie makropolecenia no-op i wykonuje operacje na łańcuchach. To narzędzie jest następnie wstawiane jako część procesu kompilacji. Podczas kompilacji te nie wywoływane funkcje makr są automatycznie analizowane w wynikach, a następnie wstawiane z powrotem jako zwykłe stare ciągi czasu kompilacji w programie. Program można następnie skompilować jak zwykle. W rzeczywistości ten wynikowy program jest również znacznie bardziej przenośny, nie wymagając wymyślnego nowego kompilatora obsługującego C ++ 11.