Skip to content

Instantly share code, notes, and snippets.

@limitedeternity
Last active November 22, 2023 13:57
Show Gist options
  • Save limitedeternity/3d548cf16c1d961ade6bdedacfd326a3 to your computer and use it in GitHub Desktop.
Save limitedeternity/3d548cf16c1d961ade6bdedacfd326a3 to your computer and use it in GitHub Desktop.
Locate C++ classes marked with __attribute__((annotate("..."))) using libclang
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)
#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