class generation via macro



  • Hey, ich würde gerne ein bischen reflection in c++ einbauen. Es brauch gar keine dynamischen funktionsaufrufe oder sonstiges, ich würde einfach nur gerne member offsets nach name erfragen. In etwa sowas:

    struct MyStruct {
    	int a;
    	double b;
    	std::string c;
    	static struct init {
    		init() {
    			somwhereglobal::RegisterOffset("MyStruct", "a", offsetof(MyStruct, a));
    			somwhereglobal::RegisterOffset("MyStruct", "b", offsetof(MyStruct, b));
    			somwhereglobal::RegisterOffset("MyStruct", "v", offsetof(MyStruct, c));
    		} 
    	} _initializer;
    };
    MyStruct::init MyStruct::initializer;
    

    Mehr brauch ich gar nicht, das würde ich allerdings gerne als macro haben und da scheiter ich kollosal 😃 Hat vielleicht wer ne idee?

    Hatte an sowas gedacht:
    MAKE_EVENT(MyStruct, int a, double b, std::string c, ...)

    Sollte am besten in msvc und gcc/clang laufen.



  • Fragwürdig, wozu du das brauchst...
    Du kannst es so machen:

    class IReflective
    {
    private:
        std::map<std::string, ptrdiff_t> MemberMap;
    protected:
        void RegisterMember(const char *name, ptrdiff_t offset)
        {
            MemberMap[name] = offset; //oder sonstige daten
        }
    public:
        ptrdiff_t getMemberOffset(const char *name) {....}
    };
    
    class RadClass : public IReflective
    {
    public:
        RadClass()
        {
            RegisterMember(...);
        }
    };
    


  • es geht mir eher um das macro, sorry wenn das nicht richtig rüber kam.

    Das brauche ich um konvertierung zwischen membern von events zu machen welche in einer ini (als strings) definiert werden. Da kann ich dann einfach eine typensichere (ok sieht man hier nicht) konvertierung zwischen beliebigen (passenden) membern der events machen. Ich hab nur kein bock immer so viel tipparbeit/wartungs aufwand zu haben und dauernd zu vergessen doch irgendweo noch was anzupassen.



  • Member könne nicht dynamisch definiert werden. Was du brauchst ist std::map und eine simple hierarchie von typ-klassen (string, int), welcher einer basisklasse, einem typinterface, angehören.



  • ahh, funktioniert doch alles. Ich krieg nur das macro nicht hin... 😕



  • Du brauchst kein Macro. Wirklich nicht. Schildere noch mal ausführlicher, was du vorhast.



  • Mein vorhaben:

    Ich möchte eine klasse per makro definieren. Das makro sollte so aussehen:

    // create a class named MyEvent, with three members
    MAKE_EVENT(MyEvent, int a, int b, double c)
    

    rauskommen soll das:

    struct MyEvent{
        int a;
        int b;
        double c;
        // kann auch extern
        static struct init {
            init() {
                // should only be called once for each type
                reflect::RegisterOffset<int>("MyStruct", "a", offsetof(MyStruct, a));
                reflect::RegisterOffset<int>("MyStruct", "b", offsetof(MyStruct, b));
                reflect::RegisterOffset<double>("MyStruct", "b", offsetof(MyStruct, c));
            }
        } _initializer;
    };
    MyStruct::init MyStruct::initializer;
    

    So, das kann man auch anders lösen, ich weiß. Aber darum geht es mir gar nicht. Ich möchte mir sich wiederholende tipparbeit sparen und daher gerne die gezeigte klasse aus dem obrigen macro generieren.

    So und wie mach ich das jetzt ohne tipparbeit und ohne macro?



  • Du brauchst doch dann für N Parameter auch N Makro Definitionen (oder?). Und ist N wirklich deutlich kleiner als X benötige Strukturen? ^^

    Was möchtest Du den mit der Reflection erreichen? Evtl. gibt es für dieses Ziel auch einen anderen weg.

    Um Tipparbeit zu ersparen sehe ich auch nur Makro oder ein Skript für die Generierung der Klassen.



  • Ok also ganz ausführlich 🙂 Ich habe eine funktionen die so aussehen:

    // handlers
    void HandleEvent(const BeginContact& event) {}
    void HandleEvent(const EndContact& event) {}
    ...
    
    // events
    struct BeginContact {
    	vec2 contactPos;
    };
    struct EndContact {};
    struct PlaySound{
    	vec2 soundPos;
    };
    

    dann kann in der funktion z.B. wieder ein Event geworfen werden:

    void HandleEvent(const BeginContact& event) 
    {
    	context->PostEvent(PlaySound());
    }
    

    jetzt kommt das ich gerne ein dynamisches mapping von event auf event hätte, also per factory:

    void HandleEvent(const BeginContact& event) 
    {
    	context->PostEvent(Factory::Make("PlaySound"));
    }
    

    jetzt kommt aber noch dazu das ich gerne einzelne properties der events mappen würde. z.B.:

    void HandleEvent(const BeginContact& event) 
    {
    	std::unique_ptr<Eventbase> eb = Factory::Make("PlaySound");
    	// wird dynamisch aus einer datei gelesen
    	map(&event, "contactPos", eb, "soundPos");
    	context->PostEvent(eb);
    }
    

    Dafür speicher ich mir typeninformationen sowie offsets von den einzelnen events und rechne dann die addressen der variablen pro object aus. Enums behandle ich als underlying type und pointer werden generell als void* behandelt, da die events niemals owner sind. Alle anderen typen müssen spezialisiert werden, aber da gibts nicht so viele daher ist das noch gut überschaubar. Das funktioniert auch alles. Ich hätte jetzt nur gerne eine mechanismus um die offsets mit den typeninformationen automatisch zu generieren.

    Das ist mein erster naiver ansatz an dem ich gerade rumspiele, einige sachen sind vielleicht gerade doppelt geprüft oder so, bastel halt gerade drann. Vielleicht geht das ja auch noch viel besser? Braucht leider Gerade glm zum kompilieren, wenn das wirklich wen interessiert einfach glm header raus und die glm sachen durch was anderes ersetzen. Z.B. std::vector<double> oder so. Bisher nur in msvc getestet.

    #include <string>
    #include <iostream>
    #include <map>
    #include <vector>
    #include <tuple>
    #include <type_traits>
    
    #include <glm/glm.hpp>
    
    // maybe we can improve this:
    // - http://stackoverflow.com/questions/672843/can-templates-be-used-to-access-struct-variables-by-name
    // - http://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer
    // - http://duriansoftware.com/joe/Self-aware-struct-like-types-in-C++11.html
    // - (maybe) http://www.nanoant.com/c++/cpp-field-accessors-cure-for-public-field-fobia
    // - http://www.ocoudert.com/blog/2011/07/09/a-practical-guide-to-c-serialization/
    // - https://www.reddit.com/r/gamedev/comments/3lh0ba/using_clang_to_generate_c_reflection_data/
    // - https://bitbucket.org/dwilliamson/clreflect/overview
    // - http://www.gdcvault.com/play/1015586/
    // - http://www.codeproject.com/Articles/318690/C-Non-intrusive-enum-class-with-reflection-supp
    
    enum Enum1
    {
    	ONE,
    	TWO
    };
    
    enum class Enum2
    {
    	THREE,
    	FOUR
    };
    
    struct EventBase 
    {
    //	virtual ~EventBase() {}
    
    // 	virtual std::string Serialize() const {}
    // 	virtual void UnSerialize(const std::string& msg) {}
    // 
    // 	virtual std::vector<uint8_t> SerializeBinary() {}
    // 	virtual void SerializeBinary(const std::vector<uint8_t>& data) {}
    };
    
    struct EventOne : public EventBase
    {
    	EventOne(std::string content1, double val1, int val2, Enum1 enm, std::vector<int>* ptr)
    		: content1(content1), val1(val1), val2(val2), enm(enm), ptr(ptr)
    	{}
    
    	std::string content1;
    	double val1;
    	int val2;
    	Enum1 enm;
    	std::vector<int>* ptr;
    };
    
    struct EventTwo : public EventBase
    {
    	EventTwo(int val1, std::string content2, glm::mat4 model, glm::u8vec4 color, Enum2 enm, std::vector<int>* ptr)
    		: val1(val1), content2(content2), model(model), color(color), enm(enm), ptr(ptr)
    	{}
    
    	int val1;
    	std::string content2;
    	glm::mat4 model;
    	glm::u8vec4 color;
    	Enum2 enm;
    	std::vector<int>* ptr;
    };
    
    struct TypedTranslatorInterface;
    struct TypedOffsetBase 
    {
    	TypedOffsetBase(size_t offset, const char* typeID, const char* convTypeID)
    		: offset(offset), typeID(typeID), convTypeID(convTypeID)
    	{}
    
    	virtual void Accept(TypedTranslatorInterface* translator) = 0;
    
    	size_t offset;
    	const char* typeID;
    	const char* convTypeID;
    };
    
    template<typename T>
    struct TypedOffset : public TypedOffsetBase
    {
    	TypedOffset()
    		: TypedOffsetBase(-1, "", "")
    	{}
    
    	TypedOffset(size_t offset, const char* typeID, const char* convTypeID)
    		: TypedOffsetBase(offset, typeID, convTypeID)
    	{}
    
    	void Accept(TypedTranslatorInterface* translator)
    	{
    		translator->Visit(this);
    	}
    };
    
    struct TypedTranslatorInterface
    {
    	TypedTranslatorInterface()
    		: eb1(nullptr), eb2(nullptr)
    	{}
    
    	virtual void Run(EventBase* eb1, TypedOffsetBase *tob1, EventBase* eb2, TypedOffsetBase* tob2) = 0;
    	virtual bool Success() = 0;
    
    	virtual ~TypedTranslatorInterface() {}
    
    	virtual void Visit(TypedOffset<int8_t>* val) {}
    	virtual void Visit(TypedOffset<int16_t>* val) {}
    	virtual void Visit(TypedOffset<int32_t>* val) {}
    	virtual void Visit(TypedOffset<int64_t>* val) {}
    
    	virtual void Visit(TypedOffset<uint8_t>* val) {}
    	virtual void Visit(TypedOffset<uint16_t>* val) {}
    	virtual void Visit(TypedOffset<uint32_t>* val) {}
    	virtual void Visit(TypedOffset<uint64_t>* val) {}
    
    	virtual void Visit(TypedOffset<float>* val) {}
    	virtual void Visit(TypedOffset<double>* val) {}
    
    	virtual void Visit(TypedOffset<glm::mat4>* val) {}
    	virtual void Visit(TypedOffset<glm::u8vec4>* val) {}
    
    	virtual void Visit(TypedOffset<std::string>* val) {}
    
    	virtual void Visit(TypedOffset<void*>* val) {}
    
    	EventBase* eb1;
    	EventBase* eb2;
    };
    
    template<typename T>
    struct TypedTranslator : public TypedTranslatorInterface
    {
    	TypedTranslator()
    		: hasValue(false), success(false)
    	{}
    
    	TypedTranslator(TypedOffsetBase *one, TypedOffsetBase* two)
    		: hasValue(false), success(false)
    	{
    		Run(one, two);
    	}
    
    	void Run(EventBase* eb1, TypedOffsetBase *tob1, EventBase* eb2, TypedOffsetBase* tob2)
    	{
    		hasValue = false;
    		success = false;
    
    		this->eb1 = eb1;
    		this->eb2 = eb2;
    
    		tob1->Accept(this);
    		tob2->Accept(this);
    	}
    
    	bool Success()
    	{
    		return success;
    	}
    
    	~TypedTranslator() {}
    
    	void Visit(TypedOffset<T>* val) override
    	{
    		if (!hasValue) {
    			first = *val;
    			hasValue = true;
    			return;
    		}
    
    		T* from = reflect::OffsetPtr<T>(eb1, first.offset);
    		T* to = reflect::OffsetPtr<T>(eb2, val->offset);
    		*to = *from;
    		success = true;
    	}
    
    	bool hasValue;
    	bool success;
    	TypedOffset<T> first;
    };
    
    struct TranslatorRegistry
    {
    	static TypedTranslatorInterface* Get(const char* name)
    	{
    		auto it = translators.find(name);
    		if (it != translators.end())
    		{
    			return it->second;
    		}
    		return nullptr;
    	}
    private:
    	static std::map<std::string, TypedTranslatorInterface*> Init()
    	{
    		std::map<std::string, TypedTranslatorInterface*> map;
    
    #define REGISTER_TRANSLATOR(type) \
    	map[typeid(type).name()] = new TypedTranslator<type>();
    
     		REGISTER_TRANSLATOR(int8_t);
     		REGISTER_TRANSLATOR(int16_t);
    		REGISTER_TRANSLATOR(int32_t);
     		REGISTER_TRANSLATOR(int64_t);
    
    		REGISTER_TRANSLATOR(uint8_t);
    		REGISTER_TRANSLATOR(uint16_t);
    		REGISTER_TRANSLATOR(uint32_t);
    		REGISTER_TRANSLATOR(uint64_t);
    
    		REGISTER_TRANSLATOR(float);
    		REGISTER_TRANSLATOR(double);
    
    		REGISTER_TRANSLATOR(glm::mat4);
    		REGISTER_TRANSLATOR(glm::u8vec4);
    
    		REGISTER_TRANSLATOR(std::string);
    		REGISTER_TRANSLATOR(std::string);
    
    		REGISTER_TRANSLATOR(void*);
    
    		return map;
    	}
    
    	static std::map<std::string, TypedTranslatorInterface*> translators;
    };
    std::map<std::string, TypedTranslatorInterface*> TranslatorRegistry::translators = TranslatorRegistry::Init();
    
    namespace reflect 
    {
    	struct Reflector
    	{
    		static std::map<std::string, std::map<std::string, TypedOffsetBase*>> offsets;
    		static void RegisterOffset(const std::string& className, const std::string& varName, TypedOffsetBase* typedOffset) {
    			offsets[className][varName] = typedOffset;
    		}
    		static TypedOffsetBase* GetOffset(const std::string& className, const std::string& varName) {
    			return offsets[className][varName];
    		}
    	};
    
    	//////////////////////////////////////////////////////////////////////////
    
    	template<typename T>
    	static typename std::enable_if<!std::is_enum<T>::value && !std::is_pointer<T>::value, void>::type
    		RegisterOffset(const char* className, const char* varName, size_t offset)
    	{
    		// types must match
    		reflect::Reflector::RegisterOffset(className, varName, new TypedOffset<T>(offset, typeid(T).name(), typeid(T).name()));
    	}
    
    	template<typename T>
    	static typename std::enable_if<std::is_enum<T>::value, void>::type
    		RegisterOffset(const char* className, const char* varName, size_t offset)
    	{
    		typedef std::underlying_type<T>::type underlying_type;
    		// the types must match but will be converted as the underlying_type
    		reflect::Reflector::RegisterOffset(className, varName, new TypedOffset<underlying_type>(offset, typeid(T).name(), typeid(underlying_type).name()));
    	}
    
    	// we want to keep the void pointer, but still the typeinfo for type checking
    	template<typename T>
    	static typename std::enable_if<std::is_pointer<T>::value, void>::type
    		RegisterOffset(const char* className, const char* varName, size_t offset)
    	{
    		// types must match but translation is performed on void* ptr, only the pointer will be changed
    		// not the value
    		reflect::Reflector::RegisterOffset(className, varName, new TypedOffset<void*>(offset, typeid(T).name(), typeid(void*).name()));
    	}
    
    	template<typename T>
    	T* OffsetPtr(void* ptr, size_t offset)
    	{
    		return reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(ptr) + offset);
    	};
    
    	std::map<std::string, std::map<std::string, TypedOffsetBase*>> Reflector::offsets = std::map<std::string, std::map<std::string, TypedOffsetBase*>>();
    }
    
    EventBase* createEvent(const std::string& name)
    {
    	return new EventTwo(6, "blah", glm::mat4(1), glm::u8vec4(128), Enum2::THREE, nullptr);
    }
    
    template<typename T>
    void PostEvent(T event)
    {
    }
    
    void HandleEvent(EventOne& event)
    {
    	// translate list
    	std::map<std::string, std::map<std::string, std::vector<std::tuple<std::string, std::string>>>> eventMapping;
    	eventMapping["EventOne"]["EventTwo"].push_back(std::make_tuple("ptr", "ptr"));
    
    	for (auto& outEvent : eventMapping["EventOne"])
    	{
    		auto outEventName = outEvent.first;
    		EventBase* eb = createEvent(outEventName);
    
    		auto varMapList = outEvent.second;
    		for (auto& varMapping : varMapList)
    		{
    			TypedOffsetBase* offsetFrom = reflect::Reflector::GetOffset("EventOne", "ptr");
    			TypedOffsetBase* offsetTo = reflect::Reflector::GetOffset("EventTwo", "ptr");
    
    			// the real typeid should be the same for both types
    			if (offsetFrom->typeID == offsetTo->typeID)
    			{
    				// type id and conversion types can differ. e.g. all pointer types are handled by the
    				// the same void* logic, all enums by the same underlying_type logic
    				TypedTranslatorInterface* tt = TranslatorRegistry::Get(offsetFrom->convTypeID);
    				if (tt)
    				{
    					// run will calculate offsets for the members and call their default assignment operator
    					// only exception are pointers where no values are copied but the pointer will be changed
    					tt->Run(&event, offsetFrom, eb, offsetTo);
    					// we need to call success or otherwise we don't know if we were able
    					// to handle both types, maybe we added a new type to an event 
    					// and the translator couldn't handle it
    					if (!tt->Success())
    					{
    						// unknown type for translator
    						int a = 5;
    					}
    				}
    				else
    				{
    					// no translator registered for type
    					int a = 5;
    				}
    			}
    			else
    			{
    				// types do not match
    				int a = 5;
    			}
    
    			PostEvent(eb);
    		}
    	}
    
    }
    
     #define REGISTER_OFFSET(className, varName) \
     	reflect::RegisterOffset<decltype(className::varName)>(#className, #varName, offsetof(className, varName));
    
    int main()
    {
    	std::vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    	EventOne one = { "hello", 1.23, -5, TWO, &vec };
    
     	REGISTER_OFFSET(EventOne, content1);
     	REGISTER_OFFSET(EventOne, val1);
     	REGISTER_OFFSET(EventOne, val2);
    	REGISTER_OFFSET(EventOne, enm);
    	REGISTER_OFFSET(EventOne, ptr);
    
     	REGISTER_OFFSET(EventTwo, val1);
     	REGISTER_OFFSET(EventTwo, content2);
     	REGISTER_OFFSET(EventTwo, model);
     	REGISTER_OFFSET(EventTwo, color);
    	REGISTER_OFFSET(EventTwo, enm);
    	REGISTER_OFFSET(EventTwo, ptr);
    
    	HandleEvent(one);
    }
    


  • Wenn dann mußt du aber jeden Parameter einzeln übergeben:

    MAKE_EVENT(MyEvent, int, a, int, b, double, c)
    

    Und dann z.B. BOOST_PREPROCESSOR benutzen.

    Ich würde dafür dann aber auch eher einen Script-Generator benutzen.



  • Eine Möglichkeit wären X-Macros:
    https://en.wikipedia.org/wiki/X_Macro

    ps: Mit mehrfachem #include des selben Files + Einsatz von Makros lassen sich auch nette Dinge machen. Kann man auch mit X-Macros kombinieren.



  • Xmacros kannte ich noch nicht, thx 🙂 Ich hab mich jetzt für eine... etwas umständlichere lösung entschieden. Zumindest wenn man auf windows ist. Ich hab über libtooling clang benutzt um den src zu parsen und daraus den benötigten initialisierungs code zu bauen. Clang unter windows zu kompilieren geht super einfach aber die stl ist ein absoluter krampf und ich musste viel mit mingw rumspielen um die zum laufen zu bekommen (die VS stl klappt nur mit wenigen headern). Der eigentliche code ist aber super kurz und fast 1zu1 aus nem beispiel. Die src beispiele sind verständlich, auch wenn die doku doch zu wünschen übrig lässt. Vieles findet man nur durch ausgiebiges googeln. Wenn wer mal auf die gleiche idee kommt, hier der src.

    // https://github.com/eliben/llvm-clang-samples
    // http://eli.thegreenplace.net/tag/llvm-clang
    // http://eli.thegreenplace.net/tag/compilation
    // -> http://eli.thegreenplace.net/2014/samples-for-using-llvm-and-clang-as-a-library/
    // -> http://eli.thegreenplace.net/2015/on-parsing-c-type-declarations-and-fake-headers/
    // -> http://eli.thegreenplace.net/2014/05/21/compilation-databases-for-clang-based-tools
    // -> http://eli.thegreenplace.net/2014/05/01/modern-source-to-source-transformation-with-clang-and-libtooling/
    // -> http://eli.thegreenplace.net/2014/07/29/ast-matchers-and-clang-refactoring-tools
    // https://genericlanguage.wordpress.com/2013/08/12/newbie-guide-understanding-clang-ast-construction/
    // http://xinhuang.github.io/clang/2014/10/19/clang-tutorial-finding-declarations/
    // http://stackoverflow.com/questions/6525245/getting-clang-to-work-on-windows
    // https://llvm.org/bugs/show_bug.cgi?id=23275
    // http://here-be-braces.com/blog/building-clang-natively-on-windows-mingw
    // http://stackoverflow.com/questions/14902683/compiling-clang-on-windows
    // https://woboq.com/blog/moc-with-clang.html
    // - https://code.woboq.org/mocng/src/mocng.cpp.html
    // http://www.myopictopics.com/?p=368
    // https://github.com/sk-havok/clang-extract
    // http://www.gdcvault.com/play/1015586/
    // https://bitbucket.org/dwilliamson/clreflect/overview
    // https://github.com/AustinBrunkhorst/CPP-Reflection
    // maybe http://szelei.me/code-generator/
    // https://www.youtube.com/watch?v=yuIOGfcOH0k
    
    #include <sstream>
    #include <string>
    
    using namespace clang;
    using namespace clang::driver;
    using namespace clang::tooling;
    
    static llvm::cl::OptionCategory ToolingSampleCategory("Tooling Sample");
    
    // By implementing RecursiveASTVisitor, we can specify which AST nodes
    // we're interested in by overriding relevant methods.
    // http://clang.llvm.org/doxygen/classclang_1_1ASTDeclReader.html
    class MyASTVisitor : public RecursiveASTVisitor<MyASTVisitor> 
    {
    public:
    	MyASTVisitor(Rewriter& r) 
    		: TheRewriter(r)
    	{}
    
    	// find classes
    	bool VisitCXXRecordDecl(CXXRecordDecl *decl) 
    	{
    		// only parse structs in the "main" file, not in included files
    		SourceLocation sloc = decl->getSourceRange().getBegin();
    		// http://stackoverflow.com/questions/20658987/clang-3-5-libtooling-getting-file-name-of-a-variable-in-clangvardecl
    		if (!decl->getASTContext().getSourceManager().isInMainFile(sloc)) {
    			return true;
    		}
    
    		std::stringstream ss;
    
    		// https://github.com/d3faultdotxbe/libclang-recursiveastvisitor-tutorial-building-linking-on-debian-wheezy/blob/master/FindClassDecls.cpp
    		// theres also getQualifiedNameAsString() if needed e.g.: a::b::blaa
    		auto className = decl->getNameAsString();
    
    		ss << "\n\tstatic struct init {\n"
    			"\t\tinit() {\n";
    
    		// http://stackoverflow.com/questions/6602967/handle-c-functions-with-clang-api-to-insert-code
    		for (auto it = decl->field_begin(); it != decl->field_end(); it++)
    		{
    			auto varName = it->getNameAsString();
    			auto varType = it->getType().getCanonicalType().getAsString();
    
    			ss << "\t\t\treflect::RegisterOffset<" << varType << ">(\"" << className << "\", \"" << varName << "\", offsetof(" << className << ", " << varName << "));\n";
    		}
    		ss << "\t\t}\n"
    			"\t} _initializer;\n";
    
    		SourceLocation blockEndSloc = decl->getLocEnd();
    		TheRewriter.InsertText(blockEndSloc, ss.str(), true, true);
    
    		SourceLocation fieldEndSloc = decl->getLocEnd().getLocWithOffset(2);
    		TheRewriter.InsertText(fieldEndSloc, "\n" + className + "::init " + className + "::initializer;\n", true, true);
    
    		return true;
    	}
    
    private:
    	Rewriter &TheRewriter;
    };
    
    // Implementation of the ASTConsumer interface for reading an AST produced
    // by the Clang parser.
    class MyASTConsumer : public ASTConsumer 
    {
    public:
    	MyASTConsumer(Rewriter& r) 
    		: Visitor(r)
    	{}
    
    	// Override the method that gets called for each parsed top-level
    	// declaration.
    	bool HandleTopLevelDecl(DeclGroupRef dr) override 
    	{
    		for (DeclGroupRef::iterator b = dr.begin(), e = dr.end(); b != e; ++b) 
    		{
    			// Traverse the declaration using our AST visitor.
    			Visitor.TraverseDecl(*b);
    			//(*b)->dump();
    		}
    		return true;
    	}
    
    private:
    	MyASTVisitor Visitor;
    };
    
    // For each source file provided to the tool, a new FrontendAction is created.
    class MyFrontendAction : public ASTFrontendAction 
    {
    public:
    	MyFrontendAction() {}
    
    	void EndSourceFileAction() override 
    	{
    		SourceManager &sm = TheRewriter.getSourceMgr();
    		llvm::errs() << "** EndSourceFileAction for: " << sm.getFileEntryForID(sm.getMainFileID())->getName() << "\n";
    
    		// Now emit the rewritten buffer.
    		TheRewriter.getEditBuffer(sm.getMainFileID()).write(llvm::outs());
    	}
    
    	std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance& ci, StringRef file) override 
    	{
    		llvm::errs() << "** Creating AST consumer for: " << file << "\n";
    		TheRewriter.setSourceMgr(ci.getSourceManager(), ci.getLangOpts());
    		return llvm::make_unique<MyASTConsumer>(TheRewriter);
    	}
    
    private:
    	Rewriter TheRewriter;
    };
    
    int main(int argc, const char **argv)
    {
    	CommonOptionsParser op(argc, argv, ToolingSampleCategory);
    	ClangTool Tool(op.getCompilations(), op.getSourcePathList());
    
    	// ClangTool::run accepts a FrontendActionFactory, which is then used to
    	// create new objects implementing the FrontendAction interface. Here we use
    	// the helper newFrontendActionFactory to create a default factory that will
    	// return a new MyFrontendAction object every time.
    	// To further customize this, we could create our own factory class.
    	return Tool.run(newFrontendActionFactory<MyFrontendAction>().get());
    }
    

Anmelden zum Antworten