Skip to content

Instantly share code, notes, and snippets.

@p7g
Created January 30, 2022 16:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save p7g/14e892bbf3cf05627449dd320563cc48 to your computer and use it in GitHub Desktop.
Save p7g/14e892bbf3cf05627449dd320563cc48 to your computer and use it in GitHub Desktop.
Ask clang what a struct looks like
"""Ask clang what a struct looks like
Structs like stat don't necessarily have the same fields in the same order on
different platforms. This makes writing a language with C FFI sound pretty
awful... since all the different platforms would need to be handled by the FFI
library of that language. Maybe with something like this, the C compiler could
be queried to find out what these structs look like?
"""
import io
import re
import subprocess
import tempfile
from pycparser import c_ast, c_generator, parse_file
csource = """\
#define __attribute__(X)
#define __restrict restrict
#include <sys/stat.h>
struct stat st;
"""
with tempfile.NamedTemporaryFile(suffix=".c") as in_file:
in_file.write(csource.encode("utf-8"))
in_file.seek(0)
ast = parse_file(
in_file.name,
use_cpp=True,
cpp_path="clang",
cpp_args=["-E"],
)
typedefs = {}
for node in ast:
if (
isinstance(node, c_ast.Decl)
and node.name is None
and isinstance(node.type, c_ast.Struct)
and node.type.name == "stat"
):
struct_stat = node
elif isinstance(node, c_ast.Typedef):
typedefs[node.name] = node.type.type
def resolve(ty):
if isinstance(ty, c_ast.TypeDecl):
return resolve(ty.type)
elif isinstance(ty, c_ast.ArrayDecl):
return c_ast.ArrayDecl(
type=c_ast.TypeDecl(
declname=ty.type.declname,
quals=ty.type.quals,
align=ty.type.align,
type=resolve(ty.type),
),
dim=ty.dim,
dim_quals=ty.dim_quals,
)
if not isinstance(ty, c_ast.IdentifierType) or len(ty.names) != 1:
return ty
name = ty.names[0]
if name not in typedefs:
return ty
return resolve(typedefs[name])
resolved_struct = c_ast.Struct(name="stat", decls=[])
for decl in struct_stat.type.decls:
resolved_type = resolve(decl.type)
if not isinstance(resolved_type, c_ast.ArrayDecl):
resolved_type = c_ast.TypeDecl(
declname=decl.name,
type=resolve(decl.type),
quals=getattr(decl.type, "quals", []),
align=getattr(decl.type, "align", None),
)
resolved_struct.decls.append(
c_ast.Decl(
name=decl.name,
type=resolved_type,
quals=decl.quals,
align=decl.align,
storage=decl.storage,
funcspec=decl.funcspec,
init=decl.init,
bitsize=decl.bitsize,
)
)
g = c_generator.CGenerator(reduce_parentheses=True)
print(g.visit(resolved_struct))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment