Last active
November 22, 2023 13:57
-
-
Save limitedeternity/3d548cf16c1d961ade6bdedacfd326a3 to your computer and use it in GitHub Desktop.
Locate C++ classes marked with __attribute__((annotate("..."))) using libclang
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import clang.cindex as cl | |
from dataclasses import dataclass | |
import functools | |
from typing import Iterable, Generator | |
def return_on_failure(value): | |
def decorator(func): | |
@functools.wraps(func) | |
def wrapper(*args, **kwargs): | |
try: | |
return func(*args, **kwargs) | |
except: | |
return value | |
return wrapper | |
return decorator | |
@dataclass | |
class Locator: | |
iterable: Iterable[cl.Cursor] | |
@classmethod | |
def take_all_nodes(cls, cursor: cl.Cursor): | |
return cls(cursor.walk_preorder()) | |
@classmethod | |
def take_child_nodes(cls, cursor: cl.Cursor): | |
return cls(cursor.get_children()) | |
def locate(self, *kinds: cl.CursorKind) -> Generator[cl.Cursor, None, None]: | |
yield from filter( | |
return_on_failure(False)(lambda cur: cur.kind in kinds), self.iterable | |
) | |
@dataclass | |
class Node: | |
cursor: cl.Cursor | |
def has_annotation(self, spelling: str) -> bool: | |
return any( | |
map( | |
lambda cur: cur.spelling == spelling, | |
Locator.take_child_nodes(self.cursor).locate( | |
cl.CursorKind.ANNOTATE_ATTR | |
), | |
) | |
) | |
@property | |
def fully_qualified_name(self) -> str: | |
def scope_components(cur: cl.Cursor) -> [str]: | |
parent = cur.lexical_parent | |
if parent.kind.is_declaration(): | |
return scope_components(parent) + [parent.spelling] | |
return [] | |
return "::".join(scope_components(self.cursor) + [self.cursor.spelling]) | |
index = cl.Index.create() | |
translation_unit = index.parse( | |
"MessageTypes.h", | |
args=["-x", "c++", "--std=c++20"], | |
options=cl.TranslationUnit.PARSE_INCOMPLETE | |
| cl.TranslationUnit.PARSE_SKIP_FUNCTION_BODIES, | |
) | |
message_classes = filter( | |
lambda cur: Node(cur).has_annotation("CODEGEN(Message)"), | |
Locator.take_all_nodes(translation_unit.cursor).locate( | |
cl.CursorKind.STRUCT_DECL, cl.CursorKind.CLASS_DECL | |
), | |
) | |
for cls in map(lambda cur: Node(cur).fully_qualified_name, message_classes): | |
print(cls) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <string> | |
#define EXPAND(...) EXPAND4(EXPAND4(EXPAND4(EXPAND4(__VA_ARGS__)))) | |
#define EXPAND4(...) EXPAND3(EXPAND3(EXPAND3(EXPAND3(__VA_ARGS__)))) | |
#define EXPAND3(...) EXPAND2(EXPAND2(EXPAND2(EXPAND2(__VA_ARGS__)))) | |
#define EXPAND2(...) EXPAND1(EXPAND1(EXPAND1(EXPAND1(__VA_ARGS__)))) | |
#define EXPAND1(...) __VA_ARGS__ | |
#define FOR_EACH(macro, ...) \ | |
__VA_OPT__(EXPAND(FOR_EACH_HELPER(macro, __VA_ARGS__))) | |
#define FOR_EACH_HELPER(macro, a1, ...) \ | |
macro(a1) __VA_OPT__(, FOR_EACH_AGAIN PARENS(macro, __VA_ARGS__)) | |
#define PARENS () | |
#define FOR_EACH_AGAIN() FOR_EACH_HELPER | |
#define CODEGEN(...) __attribute__((FOR_EACH(CODEGEN_HELPER, __VA_ARGS__))) | |
#define CODEGEN_HELPER(_trait_) annotate("CODEGEN(" #_trait_ ")") | |
namespace message_types | |
{ | |
struct CODEGEN(Message) ModuleStartData | |
{ | |
std::string startup_string; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment