Skip to main content
Glama
hydavinci
by hydavinci
uml_generate.py5.79 kB
""" C++ UML Class Diagram Generator ============================== Description: Recursively analyzes all C++ source and header files in the specified folder, extracts class, inheritance, and member information, and generates a UML class diagram in PlantUML format. Usage (as a script): python uml_generate.py <folder_path> - The UML diagram will be written to 'uml_output.puml' in the current directory. - Example: python uml_generate.py E:\\Edge\\src\\components\\sync\\ Usage (as a module): from uml_generate import generate_cpp_uml_from_path uml_text = generate_cpp_uml_from_path('your_cpp_project_path') print(uml_text) Dependencies: Python standard library: os, re """ import os import re from typing import List, Dict, Any def find_cpp_files(path: str) -> List[str]: """Recursively find all C++ source and header files in the given path.""" cpp_exts = ('.cpp', '.hpp', '.h', '.cc', '.cxx') files = [] for root, _, filenames in os.walk(path): for fname in filenames: if fname.endswith(cpp_exts): files.append(os.path.join(root, fname)) return files def parse_cpp_classes(file_content: str) -> List[Dict[str, Any]]: """Extract class/struct definitions, inheritance, and members from C++ code.""" class_pattern = re.compile(r'(class|struct)\s+(\w+)(\s*:\s*([^{]+))?\s*{', re.MULTILINE) member_pattern = re.compile(r'(public|private|protected):|([\w:<>,~]+\s+[\w:]+\s*(\([^)]*\))?;)', re.MULTILINE) classes = [] for class_match in class_pattern.finditer(file_content): kind = class_match.group(1) name = class_match.group(2) inherit = class_match.group(4) start = class_match.end() end = file_content.find('};', start) body = file_content[start:end] if end != -1 else '' members = [] access = 'private' if kind == 'class' else 'public' for m in member_pattern.finditer(body): if m.group(1): access = m.group(1) elif m.group(2): members.append((access, m.group(2).strip())) classes.append({ 'kind': kind, 'name': name, 'inherit': inherit, 'members': members }) return classes def strip_template(name: str) -> str: """Remove generic type parameters, e.g. Foo<Bar> -> Foo.""" return re.sub(r'<.*?>', '', name) def is_valid_identifier(name: str) -> bool: """Check if a string is a valid C++ identifier for PlantUML.""" if not name: return False if any(c in name for c in '()[]*&=,'): return False if name.startswith('std::') and '<' in name: return False if re.search(r'[^\w:]', name): return False return True def is_valid_member(member: str) -> bool: """Check if a member string is a valid PlantUML field/method declaration.""" if not member: return False if any(c in member for c in '()[]*&=,'): return False if '<' in member or '>' in member: return False if re.search(r'[^\w:; ]', member): return False return True def generate_plantuml(classes: List[Dict[str, Any]]) -> str: """Generate PlantUML class diagram from parsed class info.""" lines = ['@startuml'] for cls in classes: class_name = strip_template(cls["name"]) if not is_valid_identifier(class_name): continue kind = 'class' if cls['kind'] == 'class' else 'class' inherit = cls['inherit'] lines.append(f'{kind} {class_name} {{') for access, member in cls['members']: if not is_valid_member(member): continue prefix = '+' if access == 'public' else '-' if access == 'private' else '#' lines.append(f' {prefix} {member}') lines.append('}') if inherit: for base in re.split(r',', inherit): base = base.strip().split(' ')[-1] base = strip_template(base) if is_valid_identifier(base): lines.append(f'{base} <|-- {class_name}') lines.append('@enduml') return '\n'.join(lines) def generate_cpp_uml_from_content(file_contents: Dict[str, str]) -> str: """Generate PlantUML diagram from file contents dictionary. Args: file_contents (Dict[str, str]): Dictionary where keys are file names and values are file contents Returns: str: PlantUML diagram as a string """ all_classes = [] for filename, content in file_contents.items(): try: classes = parse_cpp_classes(content) all_classes.extend(classes) except Exception as e: print(f'Error parsing {filename}: {e}') return generate_plantuml(all_classes) def generate_cpp_uml_from_path(path: str) -> str: """Main entry: generate PlantUML diagram from all C++ files in a folder.""" cpp_files = find_cpp_files(path) all_classes = [] for f in cpp_files: try: with open(f, encoding='utf-8', errors='ignore') as file: content = file.read() classes = parse_cpp_classes(content) all_classes.extend(classes) except Exception as e: print(f'Error reading {f}: {e}') return generate_plantuml(all_classes) if __name__ == "__main__": import sys if len(sys.argv) != 2: print("Usage: python uml_generate.py <folder_path>") sys.exit(1) folder_path = sys.argv[1] uml_text = generate_cpp_uml_from_path(folder_path) output_file = "uml_output.puml" with open(output_file, "w", encoding="utf-8") as f: f.write(uml_text) print(f"UML diagram written to {output_file}")

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hydavinci/uml-diagram'

If you have feedback or need assistance with the MCP directory API, please join our Discord server