Skip to content

Instantly share code, notes, and snippets.

@dolinenkov
Last active July 31, 2024 22:06
Show Gist options
  • Save dolinenkov/87b71c83b4d5f6897d39d9bbdefd7578 to your computer and use it in GitHub Desktop.
Save dolinenkov/87b71c83b4d5f6897d39d9bbdefd7578 to your computer and use it in GitHub Desktop.
An example of the way how undocumented option of MSVC compiler, /d1reportallclasslayout, can be used to generate static reflection information for C++ sources
# an example of the way how undocumented option of MSVC compiler, /d1reportallclasslayout,
# can be used to generate static reflection information for C++ sources
import sys
import re
import subprocess
cl_exe = 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Tools\\MSVC\\14.16.27023\\bin\Hostx64\\x64\\cl.exe'
file = 'test.cc'
#
class Cxx:
class Attribute:
def __init__(self, offset, name):
self.offset = offset
self.name = name
class Class:
def __init__(self, name, size):
self.name = name
self.size = size
self.attributes = list()
class Module:
def __init__(self):
self.classes = list()
def log_v(str):
print('v: {}'.format(str))
def log_e(str):
print('e: {}'.format(str))
if __name__ == '__main__':
classesInformation = Cxx.Module()
cxx = subprocess.run([cl_exe, file, '/c', '/nologo', '/d1reportAllClassLayout'], capture_output=True)
if cxx.returncode != 0:
log_e('compiler process failed!')
classLineRegex = re.compile(r'class\s+([a-zA-Z_:][a-zA-Z0-9_:]*)\s+size\(([1-9][0-9]*)\):')
delimiterRegex = re.compile(r'\s*\+---\s*')
attributeRegex = re.compile(r'\s*([0-9]+)\s*\|\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*|([a-zA-Z_][a-zA-Z0-9_]*)\s*')
class State:
class Undefined:
pass
class Class:
pass
class Children:
pass
state = State.Undefined
classInfo = None
for line in cxx.stdout.decode('utf-8').splitlines()[1:]:
line = line.replace('\t', ' ').strip()
if not line:
continue
if state == State.Undefined:
regexMatch = classLineRegex.fullmatch(line)
if regexMatch:
name = regexMatch.group(1)
size = int(regexMatch.group(2))
log_v('class found: {}, {}'.format(name, size))
state = State.Class
classInfo = Cxx.Class(name, size)
classesInformation.classes.append(classInfo)
else:
log_e('unexpected: {}'.format(line))
continue
if state == State.Class:
if delimiterRegex.fullmatch(line):
state = State.Children
else:
log_e('unexpected: {}'.format(line))
continue
if state == State.Children:
if delimiterRegex.fullmatch(line):
state = State.Undefined
classInfo = None
continue
attributeMatch = attributeRegex.match(line)
if attributeMatch:
# size, (prefix), name
name = attributeMatch.group(3) if attributeMatch.group(3) else attributeMatch.group(2)
offset = int(attributeMatch.group(1))
log_v(' attribute {} at {}'.format(name, offset))
classInfo.attributes.append(Cxx.Attribute(offset, name))
continue
if '<alignment member>' in line:
continue
log_e('unexpected: {}'.format(line))
log_e('unexpected situation!')
sys.exit(2)
log_v('gracefully finished')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment