IncludeCPP 3.3.11__py3-none-any.whl → 3.3.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- includecpp/__init__.py +1 -1
- includecpp/cli/commands.py +265 -41
- includecpp/core/ai_integration.py +69 -34
- includecpp/core/cppy_converter.py +430 -16
- includecpp/core/error_formatter.py +50 -19
- includecpp/core/project_ui.py +2720 -0
- includecpp/core/settings_ui.py +127 -48
- {includecpp-3.3.11.dist-info → includecpp-3.3.20.dist-info}/METADATA +116 -18
- {includecpp-3.3.11.dist-info → includecpp-3.3.20.dist-info}/RECORD +13 -12
- {includecpp-3.3.11.dist-info → includecpp-3.3.20.dist-info}/WHEEL +0 -0
- {includecpp-3.3.11.dist-info → includecpp-3.3.20.dist-info}/entry_points.txt +0 -0
- {includecpp-3.3.11.dist-info → includecpp-3.3.20.dist-info}/licenses/LICENSE +0 -0
- {includecpp-3.3.11.dist-info → includecpp-3.3.20.dist-info}/top_level.txt +0 -0
|
@@ -260,6 +260,52 @@ class StructInfo:
|
|
|
260
260
|
|
|
261
261
|
|
|
262
262
|
class PythonToCppConverter:
|
|
263
|
+
# Common parameter name patterns to infer types
|
|
264
|
+
PARAM_TYPE_HINTS = {
|
|
265
|
+
# Integer-like names
|
|
266
|
+
'n': 'int', 'k': 'int', 'i': 'int', 'j': 'int', 'count': 'int', 'num': 'int',
|
|
267
|
+
'index': 'int', 'idx': 'int', 'size': 'size_t', 'length': 'size_t', 'len': 'size_t',
|
|
268
|
+
'start': 'int', 'end': 'int', 'begin': 'int', 'stop': 'int', 'step': 'int',
|
|
269
|
+
'seed': 'int', 'limit': 'int', 'offset': 'int', 'position': 'int', 'pos': 'int',
|
|
270
|
+
'width': 'int', 'height': 'int', 'depth': 'int', 'x': 'int', 'y': 'int', 'z': 'int',
|
|
271
|
+
'row': 'int', 'col': 'int', 'rows': 'int', 'cols': 'int', 'port': 'int',
|
|
272
|
+
# Float-like names
|
|
273
|
+
'alpha': 'double', 'beta': 'double', 'gamma': 'double', 'delta': 'double',
|
|
274
|
+
'mu': 'double', 'sigma': 'double', 'lambd': 'double', 'lambda_': 'double',
|
|
275
|
+
'rate': 'double', 'ratio': 'double', 'scale': 'double', 'weight': 'double',
|
|
276
|
+
'probability': 'double', 'prob': 'double', 'threshold': 'double',
|
|
277
|
+
'min_val': 'double', 'max_val': 'double',
|
|
278
|
+
'temperature': 'double', 'temp': 'double', 'factor': 'double',
|
|
279
|
+
# Template-matching parameters (for generic functions with T_CONTAINER)
|
|
280
|
+
# These will be T when used with a T_CONTAINER parameter
|
|
281
|
+
'value': 'T_ELEMENT', 'val': 'T_ELEMENT', 'item': 'T_ELEMENT', 'element': 'T_ELEMENT',
|
|
282
|
+
'target': 'T_ELEMENT', 'needle': 'T_ELEMENT', 'search': 'T_ELEMENT',
|
|
283
|
+
# String-like names
|
|
284
|
+
'name': 'std::string', 'text': 'std::string', 'msg': 'std::string', 'message': 'std::string',
|
|
285
|
+
'path': 'std::string', 'filename': 'std::string', 'file': 'std::string', 'dir': 'std::string',
|
|
286
|
+
'url': 'std::string', 'key': 'std::string', 'prefix': 'std::string', 'suffix': 'std::string',
|
|
287
|
+
'pattern': 'std::string', 'format': 'std::string', 'fmt': 'std::string',
|
|
288
|
+
'title': 'std::string', 'label': 'std::string', 'description': 'std::string', 'desc': 'std::string',
|
|
289
|
+
'content': 'std::string', 'data': 'std::string', 'input': 'std::string', 'output': 'std::string',
|
|
290
|
+
's': 'std::string', 'str': 'std::string', 'string': 'std::string', 'line': 'std::string',
|
|
291
|
+
'sep': 'std::string', 'delimiter': 'std::string', 'delim': 'std::string',
|
|
292
|
+
# Boolean-like names
|
|
293
|
+
'flag': 'bool', 'enabled': 'bool', 'disabled': 'bool', 'active': 'bool',
|
|
294
|
+
'is_valid': 'bool', 'is_empty': 'bool', 'success': 'bool', 'ok': 'bool',
|
|
295
|
+
'verbose': 'bool', 'quiet': 'bool', 'force': 'bool', 'recursive': 'bool',
|
|
296
|
+
# Container-like names - use T as template parameter marker
|
|
297
|
+
# These will trigger template generation for the containing function
|
|
298
|
+
'items': 'T_CONTAINER', 'elements': 'T_CONTAINER', 'values': 'T_CONTAINER',
|
|
299
|
+
'list': 'T_CONTAINER', 'lst': 'T_CONTAINER', 'array': 'T_CONTAINER',
|
|
300
|
+
'choices': 'T_CONTAINER', 'options': 'T_CONTAINER',
|
|
301
|
+
'population': 'T_CONTAINER', 'sample': 'T_CONTAINER',
|
|
302
|
+
'weights': 'std::vector<double>',
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
# Template parameter markers - functions with these params become templates
|
|
306
|
+
TEMPLATE_MARKER = 'T_CONTAINER'
|
|
307
|
+
TEMPLATE_ELEMENT = 'T_ELEMENT' # For parameters that match container element type
|
|
308
|
+
|
|
263
309
|
def __init__(self):
|
|
264
310
|
self.imports: Set[str] = set()
|
|
265
311
|
self.forward_decls: Set[str] = set()
|
|
@@ -267,6 +313,7 @@ class PythonToCppConverter:
|
|
|
267
313
|
self.warnings: List[str] = []
|
|
268
314
|
self.python_imports: Set[str] = set()
|
|
269
315
|
self.seeded_rngs: Dict[str, str] = {} # var_name -> seed expression
|
|
316
|
+
self.var_types: Dict[str, str] = {} # Variable name -> C++ type tracking
|
|
270
317
|
|
|
271
318
|
def convert(self, source: str, module_name: str) -> Tuple[str, str]:
|
|
272
319
|
"""Convert Python source to C++ (.cpp and .h content)."""
|
|
@@ -276,6 +323,7 @@ class PythonToCppConverter:
|
|
|
276
323
|
self.warnings = []
|
|
277
324
|
self.python_imports = set()
|
|
278
325
|
self.seeded_rngs = {}
|
|
326
|
+
self.var_types = {}
|
|
279
327
|
|
|
280
328
|
try:
|
|
281
329
|
tree = ast.parse(source)
|
|
@@ -392,6 +440,78 @@ class PythonToCppConverter:
|
|
|
392
440
|
constructors=constructors
|
|
393
441
|
)
|
|
394
442
|
|
|
443
|
+
def _infer_param_type(self, param_name: str, func_node) -> str:
|
|
444
|
+
"""Infer C++ type from parameter name and usage patterns."""
|
|
445
|
+
# Check direct name hints
|
|
446
|
+
if param_name in self.PARAM_TYPE_HINTS:
|
|
447
|
+
return self.PARAM_TYPE_HINTS[param_name]
|
|
448
|
+
|
|
449
|
+
# Check partial matches (e.g., 'file_name' contains 'name')
|
|
450
|
+
param_lower = param_name.lower()
|
|
451
|
+
for hint_name, hint_type in self.PARAM_TYPE_HINTS.items():
|
|
452
|
+
if hint_name in param_lower or param_lower.endswith('_' + hint_name):
|
|
453
|
+
return hint_type
|
|
454
|
+
|
|
455
|
+
# Analyze how the parameter is used in the function body
|
|
456
|
+
inferred = self._analyze_param_usage(param_name, func_node)
|
|
457
|
+
if inferred and inferred != 'auto':
|
|
458
|
+
return inferred
|
|
459
|
+
|
|
460
|
+
# Default: use auto (will become template)
|
|
461
|
+
return 'auto'
|
|
462
|
+
|
|
463
|
+
def _analyze_param_usage(self, param_name: str, func_node) -> str:
|
|
464
|
+
"""Analyze how a parameter is used to infer its type."""
|
|
465
|
+
for node in ast.walk(func_node):
|
|
466
|
+
# Check if used in comparison with numeric literal
|
|
467
|
+
if isinstance(node, ast.Compare):
|
|
468
|
+
left = node.left
|
|
469
|
+
if isinstance(left, ast.Name) and left.id == param_name:
|
|
470
|
+
for comp in node.comparators:
|
|
471
|
+
if isinstance(comp, ast.Constant):
|
|
472
|
+
if isinstance(comp.value, int):
|
|
473
|
+
return 'int'
|
|
474
|
+
elif isinstance(comp.value, float):
|
|
475
|
+
return 'double'
|
|
476
|
+
|
|
477
|
+
# Check if used in arithmetic operations
|
|
478
|
+
if isinstance(node, ast.BinOp):
|
|
479
|
+
left = node.left
|
|
480
|
+
right = node.right
|
|
481
|
+
if (isinstance(left, ast.Name) and left.id == param_name) or \
|
|
482
|
+
(isinstance(right, ast.Name) and right.id == param_name):
|
|
483
|
+
if isinstance(node.op, (ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod)):
|
|
484
|
+
# Check if other operand hints at type
|
|
485
|
+
other = right if isinstance(left, ast.Name) and left.id == param_name else left
|
|
486
|
+
if isinstance(other, ast.Constant):
|
|
487
|
+
if isinstance(other.value, float):
|
|
488
|
+
return 'double'
|
|
489
|
+
elif isinstance(other.value, int):
|
|
490
|
+
return 'int'
|
|
491
|
+
|
|
492
|
+
# Check if used in string operations
|
|
493
|
+
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add):
|
|
494
|
+
left = node.left
|
|
495
|
+
right = node.right
|
|
496
|
+
if isinstance(left, ast.Name) and left.id == param_name:
|
|
497
|
+
if isinstance(right, ast.Constant) and isinstance(right.value, str):
|
|
498
|
+
return 'std::string'
|
|
499
|
+
if isinstance(right, ast.Name) and right.id == param_name:
|
|
500
|
+
if isinstance(left, ast.Constant) and isinstance(left.value, str):
|
|
501
|
+
return 'std::string'
|
|
502
|
+
|
|
503
|
+
# Check if used in subscript (likely a container)
|
|
504
|
+
if isinstance(node, ast.Subscript):
|
|
505
|
+
if isinstance(node.value, ast.Name) and node.value.id == param_name:
|
|
506
|
+
return 'auto' # Container, will become template
|
|
507
|
+
|
|
508
|
+
# Check if used in for loop iteration
|
|
509
|
+
if isinstance(node, ast.For):
|
|
510
|
+
if isinstance(node.iter, ast.Name) and node.iter.id == param_name:
|
|
511
|
+
return 'auto' # Iterable, will become template
|
|
512
|
+
|
|
513
|
+
return 'auto'
|
|
514
|
+
|
|
395
515
|
def _convert_function(self, node, is_method: bool = False) -> FunctionInfo:
|
|
396
516
|
decorators = []
|
|
397
517
|
is_static = False
|
|
@@ -414,8 +534,11 @@ class PythonToCppConverter:
|
|
|
414
534
|
if arg.annotation:
|
|
415
535
|
param_type = self._convert_type_annotation(arg.annotation)
|
|
416
536
|
else:
|
|
417
|
-
|
|
537
|
+
# Use smart type inference
|
|
538
|
+
param_type = self._infer_param_type(arg.arg, node)
|
|
418
539
|
params.append((arg.arg, param_type))
|
|
540
|
+
# Track parameter types for use in body conversion
|
|
541
|
+
self.var_types[arg.arg] = param_type
|
|
419
542
|
|
|
420
543
|
if node.returns:
|
|
421
544
|
return_type = self._convert_type_annotation(node.returns)
|
|
@@ -590,6 +713,82 @@ class PythonToCppConverter:
|
|
|
590
713
|
return self._infer_type(stmt.value)
|
|
591
714
|
return 'void'
|
|
592
715
|
|
|
716
|
+
def _track_for_loop_var_type(self, var_name: str, iter_node) -> None:
|
|
717
|
+
"""Track the type of a for loop variable based on the iterable."""
|
|
718
|
+
iter_type = self._infer_type(iter_node)
|
|
719
|
+
|
|
720
|
+
# Extract element type from container types
|
|
721
|
+
if '<' in iter_type and '>' in iter_type:
|
|
722
|
+
element_type = iter_type[iter_type.find('<')+1:iter_type.rfind('>')]
|
|
723
|
+
self.var_types[var_name] = element_type
|
|
724
|
+
elif 'string' in iter_type.lower():
|
|
725
|
+
# Iterating over string gives char
|
|
726
|
+
self.var_types[var_name] = 'char'
|
|
727
|
+
elif isinstance(iter_node, ast.Name):
|
|
728
|
+
# Check if iterable is a known variable
|
|
729
|
+
known_type = self.var_types.get(iter_node.id, '')
|
|
730
|
+
if '<' in known_type and '>' in known_type:
|
|
731
|
+
element_type = known_type[known_type.find('<')+1:known_type.rfind('>')]
|
|
732
|
+
self.var_types[var_name] = element_type
|
|
733
|
+
elif 'string' in known_type.lower():
|
|
734
|
+
self.var_types[var_name] = 'std::string'
|
|
735
|
+
else:
|
|
736
|
+
self.var_types[var_name] = 'auto'
|
|
737
|
+
else:
|
|
738
|
+
self.var_types[var_name] = 'auto'
|
|
739
|
+
|
|
740
|
+
def _is_string_expr(self, node, expr: str) -> bool:
|
|
741
|
+
"""Check if an expression evaluates to a string type.
|
|
742
|
+
|
|
743
|
+
This prevents wrapping string values in std::to_string() in f-strings.
|
|
744
|
+
"""
|
|
745
|
+
# Check obvious string indicators in converted expression
|
|
746
|
+
if any(x in expr for x in ['"', '.c_str()', 'std::string', '_str']):
|
|
747
|
+
return True
|
|
748
|
+
|
|
749
|
+
# Check if it's a string literal constant
|
|
750
|
+
if isinstance(node, ast.Constant) and isinstance(node.value, str):
|
|
751
|
+
return True
|
|
752
|
+
|
|
753
|
+
# Check if it's a variable we know is a string
|
|
754
|
+
if isinstance(node, ast.Name):
|
|
755
|
+
var_name = node.id
|
|
756
|
+
var_type = self.var_types.get(var_name, '')
|
|
757
|
+
if 'string' in var_type.lower() or var_type == 'std::string':
|
|
758
|
+
return True
|
|
759
|
+
# Check if variable name suggests string type
|
|
760
|
+
if var_name in self.PARAM_TYPE_HINTS:
|
|
761
|
+
if 'string' in self.PARAM_TYPE_HINTS[var_name].lower():
|
|
762
|
+
return True
|
|
763
|
+
# Common string variable names
|
|
764
|
+
string_names = {'name', 'text', 'msg', 'message', 'path', 'url', 'title',
|
|
765
|
+
'label', 'description', 'content', 'data', 'line', 's', 'str',
|
|
766
|
+
'item', 'key', 'value', 'word', 'char', 'prefix', 'suffix',
|
|
767
|
+
'filename', 'file', 'dir', 'result', 'output', 'input'}
|
|
768
|
+
if var_name.lower() in string_names or any(var_name.lower().endswith('_' + n) for n in string_names):
|
|
769
|
+
return True
|
|
770
|
+
|
|
771
|
+
# Check string method calls like .upper(), .strip(), etc.
|
|
772
|
+
if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute):
|
|
773
|
+
string_methods = {'upper', 'lower', 'strip', 'lstrip', 'rstrip', 'title',
|
|
774
|
+
'capitalize', 'replace', 'format', 'join', 'split',
|
|
775
|
+
'encode', 'decode', 'center', 'ljust', 'rjust'}
|
|
776
|
+
if node.func.attr in string_methods:
|
|
777
|
+
return True
|
|
778
|
+
|
|
779
|
+
# Check attribute access that returns string (like .name, .path)
|
|
780
|
+
if isinstance(node, ast.Attribute):
|
|
781
|
+
string_attrs = {'name', 'path', 'filename', 'text', 'message', 'value'}
|
|
782
|
+
if node.attr in string_attrs:
|
|
783
|
+
return True
|
|
784
|
+
|
|
785
|
+
# Check inferred type
|
|
786
|
+
inferred_type = self._infer_type(node)
|
|
787
|
+
if 'string' in inferred_type.lower():
|
|
788
|
+
return True
|
|
789
|
+
|
|
790
|
+
return False
|
|
791
|
+
|
|
593
792
|
def _convert_body(self, stmts: List, indent: int = 1) -> str:
|
|
594
793
|
lines = []
|
|
595
794
|
ind = ' ' * indent
|
|
@@ -637,6 +836,8 @@ class PythonToCppConverter:
|
|
|
637
836
|
value_str = self._convert_expr(stmt.value)
|
|
638
837
|
var_type = self._infer_type(stmt.value)
|
|
639
838
|
if isinstance(target, ast.Name):
|
|
839
|
+
# Track this variable's type
|
|
840
|
+
self.var_types[target.id] = var_type
|
|
640
841
|
lines.append(f'{ind}{var_type} {target_str} = {value_str};')
|
|
641
842
|
else:
|
|
642
843
|
lines.append(f'{ind}{target_str} = {value_str};')
|
|
@@ -644,6 +845,9 @@ class PythonToCppConverter:
|
|
|
644
845
|
elif isinstance(stmt, ast.AnnAssign):
|
|
645
846
|
target_str = self._convert_expr(stmt.target)
|
|
646
847
|
var_type = self._convert_type_annotation(stmt.annotation)
|
|
848
|
+
# Track annotated variable types
|
|
849
|
+
if isinstance(stmt.target, ast.Name):
|
|
850
|
+
self.var_types[stmt.target.id] = var_type
|
|
647
851
|
if stmt.value:
|
|
648
852
|
value_str = self._convert_expr(stmt.value)
|
|
649
853
|
lines.append(f'{ind}{var_type} {target_str} = {value_str};')
|
|
@@ -657,8 +861,17 @@ class PythonToCppConverter:
|
|
|
657
861
|
lines.append(f'{ind}{target_str} {op}= {value_str};')
|
|
658
862
|
|
|
659
863
|
elif isinstance(stmt, ast.Expr):
|
|
660
|
-
|
|
661
|
-
|
|
864
|
+
# Check if this is a docstring (string literal expression) - convert to comment
|
|
865
|
+
if isinstance(stmt.value, ast.Constant) and isinstance(stmt.value.value, str):
|
|
866
|
+
docstring = stmt.value.value.strip()
|
|
867
|
+
if docstring:
|
|
868
|
+
# Convert to C++ comment (take first line only for brevity)
|
|
869
|
+
first_line = docstring.split('\n')[0].strip()
|
|
870
|
+
if first_line:
|
|
871
|
+
lines.append(f'{ind}// {first_line}')
|
|
872
|
+
else:
|
|
873
|
+
expr_str = self._convert_expr(stmt.value)
|
|
874
|
+
lines.append(f'{ind}{expr_str};')
|
|
662
875
|
|
|
663
876
|
elif isinstance(stmt, ast.If):
|
|
664
877
|
test = self._convert_expr(stmt.test)
|
|
@@ -697,6 +910,7 @@ class PythonToCppConverter:
|
|
|
697
910
|
elif iter_expr.func.id == 'enumerate':
|
|
698
911
|
# Handle enumerate(items, start=N)
|
|
699
912
|
args = iter_expr.args
|
|
913
|
+
iterable_node = args[0] if args else None
|
|
700
914
|
iterable = self._convert_expr(args[0]) if args else 'items'
|
|
701
915
|
start_idx = '0'
|
|
702
916
|
# Check for start keyword arg
|
|
@@ -709,6 +923,32 @@ class PythonToCppConverter:
|
|
|
709
923
|
if isinstance(stmt.target, ast.Tuple) and len(stmt.target.elts) == 2:
|
|
710
924
|
idx_name = self._convert_expr(stmt.target.elts[0])
|
|
711
925
|
item_name = self._convert_expr(stmt.target.elts[1])
|
|
926
|
+
|
|
927
|
+
# Track variable types for the loop variables
|
|
928
|
+
self.var_types[idx_name] = 'size_t'
|
|
929
|
+
|
|
930
|
+
# Infer item type from the iterable
|
|
931
|
+
if iterable_node:
|
|
932
|
+
iterable_type = self._infer_type(iterable_node)
|
|
933
|
+
# Extract element type from container (e.g., std::vector<std::string> -> std::string)
|
|
934
|
+
if '<' in iterable_type and '>' in iterable_type:
|
|
935
|
+
element_type = iterable_type[iterable_type.find('<')+1:iterable_type.rfind('>')]
|
|
936
|
+
self.var_types[item_name] = element_type
|
|
937
|
+
else:
|
|
938
|
+
# Check if the iterable is a known variable
|
|
939
|
+
if isinstance(iterable_node, ast.Name):
|
|
940
|
+
var_type = self.var_types.get(iterable_node.id, '')
|
|
941
|
+
if 'string' in var_type.lower():
|
|
942
|
+
self.var_types[item_name] = 'std::string'
|
|
943
|
+
elif var_type == self.TEMPLATE_MARKER or 'std::vector' in var_type:
|
|
944
|
+
self.var_types[item_name] = 'auto'
|
|
945
|
+
else:
|
|
946
|
+
self.var_types[item_name] = 'auto'
|
|
947
|
+
else:
|
|
948
|
+
self.var_types[item_name] = 'auto'
|
|
949
|
+
else:
|
|
950
|
+
self.var_types[item_name] = 'auto'
|
|
951
|
+
|
|
712
952
|
lines.append(f'{ind}size_t {idx_name} = {start_idx};')
|
|
713
953
|
lines.append(f'{ind}for (auto& {item_name} : {iterable}) {{')
|
|
714
954
|
# Add index increment at end of loop body
|
|
@@ -720,12 +960,21 @@ class PythonToCppConverter:
|
|
|
720
960
|
else:
|
|
721
961
|
# Fallback: just iterate
|
|
722
962
|
iter_str = self._convert_expr(iter_expr)
|
|
963
|
+
# Track loop variable type from iterable
|
|
964
|
+
if isinstance(stmt.target, ast.Name):
|
|
965
|
+
self._track_for_loop_var_type(stmt.target.id, iter_expr)
|
|
723
966
|
lines.append(f'{ind}for (auto& {target} : {iter_str}) {{')
|
|
724
967
|
else:
|
|
725
968
|
iter_str = self._convert_expr(iter_expr)
|
|
969
|
+
# Track loop variable type from iterable
|
|
970
|
+
if isinstance(stmt.target, ast.Name):
|
|
971
|
+
self._track_for_loop_var_type(stmt.target.id, iter_expr)
|
|
726
972
|
lines.append(f'{ind}for (auto& {target} : {iter_str}) {{')
|
|
727
973
|
else:
|
|
728
974
|
iter_str = self._convert_expr(iter_expr)
|
|
975
|
+
# Track loop variable type from iterable
|
|
976
|
+
if isinstance(stmt.target, ast.Name):
|
|
977
|
+
self._track_for_loop_var_type(stmt.target.id, iter_expr)
|
|
729
978
|
lines.append(f'{ind}for (auto& {target} : {iter_str}) {{')
|
|
730
979
|
|
|
731
980
|
lines.extend(self._convert_stmt_list(stmt.body, indent + 1))
|
|
@@ -962,14 +1211,24 @@ class PythonToCppConverter:
|
|
|
962
1211
|
elif func == 'abs':
|
|
963
1212
|
return f'std::abs({_safe_arg(args, 0)})'
|
|
964
1213
|
elif func == 'min':
|
|
1214
|
+
self.imports.add('<algorithm>')
|
|
965
1215
|
if len(args) == 2:
|
|
966
1216
|
return f'std::min({_safe_arg(args, 0)}, {_safe_arg(args, 1)})'
|
|
1217
|
+
elif len(args) == 1:
|
|
1218
|
+
# Single container argument - use min_element
|
|
1219
|
+
arg0 = _safe_arg(args, 0)
|
|
1220
|
+
return f'*std::min_element({arg0}.begin(), {arg0}.end())'
|
|
967
1221
|
elif args:
|
|
968
1222
|
return f'std::min({{{", ".join(args)}}})'
|
|
969
1223
|
return '0 /* min() requires arguments */'
|
|
970
1224
|
elif func == 'max':
|
|
1225
|
+
self.imports.add('<algorithm>')
|
|
971
1226
|
if len(args) == 2:
|
|
972
1227
|
return f'std::max({_safe_arg(args, 0)}, {_safe_arg(args, 1)})'
|
|
1228
|
+
elif len(args) == 1:
|
|
1229
|
+
# Single container argument - use max_element
|
|
1230
|
+
arg0 = _safe_arg(args, 0)
|
|
1231
|
+
return f'*std::max_element({arg0}.begin(), {arg0}.end())'
|
|
973
1232
|
elif args:
|
|
974
1233
|
return f'std::max({{{", ".join(args)}}})'
|
|
975
1234
|
return '0 /* max() requires arguments */'
|
|
@@ -1239,8 +1498,9 @@ class PythonToCppConverter:
|
|
|
1239
1498
|
elif isinstance(value, ast.FormattedValue):
|
|
1240
1499
|
# {expr} part - convert to std::to_string or direct if already string
|
|
1241
1500
|
expr = self._convert_expr(value.value)
|
|
1242
|
-
# Check if it's
|
|
1243
|
-
|
|
1501
|
+
# Check if it's a string type (no std::to_string needed)
|
|
1502
|
+
is_string_type = self._is_string_expr(value.value, expr)
|
|
1503
|
+
if is_string_type:
|
|
1244
1504
|
parts.append(expr)
|
|
1245
1505
|
else:
|
|
1246
1506
|
parts.append(f'std::to_string({expr})')
|
|
@@ -1304,10 +1564,83 @@ class PythonToCppConverter:
|
|
|
1304
1564
|
def _format_params(self, params: List[Tuple[str, str]]) -> str:
|
|
1305
1565
|
return ', '.join(f'{ptype} {pname}' for pname, ptype in params)
|
|
1306
1566
|
|
|
1307
|
-
def
|
|
1567
|
+
def _needs_template(self, params: List[Tuple[str, str]]) -> bool:
|
|
1568
|
+
"""Check if any parameter requires a template (has T_CONTAINER or T_ELEMENT type)."""
|
|
1569
|
+
return any(ptype in (self.TEMPLATE_MARKER, self.TEMPLATE_ELEMENT) for _, ptype in params)
|
|
1570
|
+
|
|
1571
|
+
def _has_container_param(self, params: List[Tuple[str, str]]) -> bool:
|
|
1572
|
+
"""Check if params include a T_CONTAINER (useful for element type matching)."""
|
|
1573
|
+
return any(ptype == self.TEMPLATE_MARKER for _, ptype in params)
|
|
1574
|
+
|
|
1575
|
+
def _get_template_return_type(self, return_type: str, body: str, params: List[Tuple[str, str]]) -> str:
|
|
1576
|
+
"""Determine the proper return type for a template function."""
|
|
1577
|
+
# If return type is already explicit, use it
|
|
1578
|
+
if return_type not in ('auto', 'void'):
|
|
1579
|
+
return return_type
|
|
1580
|
+
if return_type == 'void':
|
|
1581
|
+
return 'void'
|
|
1582
|
+
|
|
1583
|
+
# Get container parameter names
|
|
1584
|
+
container_params = [pname for pname, ptype in params if ptype == self.TEMPLATE_MARKER]
|
|
1585
|
+
|
|
1586
|
+
# Check if body returns one of the container parameters (e.g., shuffle_list returns items)
|
|
1587
|
+
# Look for patterns like "return items" where items is a container param
|
|
1588
|
+
for pname in container_params:
|
|
1589
|
+
if re.search(rf'\breturn\s+{re.escape(pname)}\b', body):
|
|
1590
|
+
return 'std::vector<T>'
|
|
1591
|
+
|
|
1592
|
+
# Default to T for functions that return an element
|
|
1593
|
+
return 'T'
|
|
1594
|
+
|
|
1595
|
+
def _generate_explicit_instantiation(self, func_name: str, params: List[Tuple[str, str]],
|
|
1596
|
+
return_type: str, class_name: str = None,
|
|
1597
|
+
const: str = '') -> List[str]:
|
|
1598
|
+
"""Generate explicit template instantiations for common types."""
|
|
1599
|
+
lines = ['// Explicit instantiations']
|
|
1600
|
+
|
|
1601
|
+
has_element_param = any(ptype == self.TEMPLATE_ELEMENT for _, ptype in params)
|
|
1602
|
+
|
|
1603
|
+
for cpp_type in ['int', 'double', 'std::string']:
|
|
1604
|
+
# Build parameter list for instantiation
|
|
1605
|
+
inst_params = []
|
|
1606
|
+
for pname, ptype in params:
|
|
1607
|
+
if ptype == self.TEMPLATE_MARKER:
|
|
1608
|
+
inst_params.append(f'const std::vector<{cpp_type}>&')
|
|
1609
|
+
elif ptype == self.TEMPLATE_ELEMENT:
|
|
1610
|
+
inst_params.append(f'const {cpp_type}&')
|
|
1611
|
+
# Skip non-template params in explicit instantiation signature
|
|
1612
|
+
|
|
1613
|
+
params_str = ', '.join(inst_params)
|
|
1614
|
+
|
|
1615
|
+
# Determine return type for instantiation
|
|
1616
|
+
if return_type == 'std::vector<T>':
|
|
1617
|
+
inst_ret = f'std::vector<{cpp_type}>'
|
|
1618
|
+
elif return_type == 'T':
|
|
1619
|
+
inst_ret = cpp_type
|
|
1620
|
+
else:
|
|
1621
|
+
inst_ret = return_type
|
|
1622
|
+
|
|
1623
|
+
if class_name:
|
|
1624
|
+
lines.append(f'template {inst_ret} {class_name}::{func_name}<{cpp_type}>({params_str}){const};')
|
|
1625
|
+
else:
|
|
1626
|
+
lines.append(f'template {inst_ret} {func_name}<{cpp_type}>({params_str});')
|
|
1627
|
+
|
|
1628
|
+
return lines
|
|
1629
|
+
|
|
1630
|
+
def _format_params_with_const_ref(self, params: List[Tuple[str, str]], use_template: bool = False) -> str:
|
|
1631
|
+
has_container = self._has_container_param(params)
|
|
1308
1632
|
result = []
|
|
1309
1633
|
for pname, ptype in params:
|
|
1310
|
-
|
|
1634
|
+
# Handle template container marker
|
|
1635
|
+
if ptype == self.TEMPLATE_MARKER:
|
|
1636
|
+
result.append(f'const std::vector<T>& {pname}')
|
|
1637
|
+
# Handle template element marker - only becomes T if there's also a container param
|
|
1638
|
+
elif ptype == self.TEMPLATE_ELEMENT:
|
|
1639
|
+
if has_container:
|
|
1640
|
+
result.append(f'const T& {pname}')
|
|
1641
|
+
else:
|
|
1642
|
+
result.append(f'double {pname}') # Fallback to double if no container context
|
|
1643
|
+
elif ptype in ('std::string', 'std::vector', 'std::unordered_map', 'std::unordered_set') or ptype.startswith('std::'):
|
|
1311
1644
|
result.append(f'const {ptype}& {pname}')
|
|
1312
1645
|
else:
|
|
1313
1646
|
result.append(f'{ptype} {pname}')
|
|
@@ -1346,7 +1679,25 @@ class PythonToCppConverter:
|
|
|
1346
1679
|
lines.append(f'class {cls.name} {{')
|
|
1347
1680
|
lines.append('public:')
|
|
1348
1681
|
|
|
1349
|
-
for fname, ftype,
|
|
1682
|
+
for fname, ftype, fval in cls.fields:
|
|
1683
|
+
# 'auto' is invalid for class members - infer from value or use default
|
|
1684
|
+
if ftype == 'auto':
|
|
1685
|
+
if fval:
|
|
1686
|
+
# Try to infer from the default value expression
|
|
1687
|
+
if fval.startswith('"') or fval.startswith("'"):
|
|
1688
|
+
ftype = 'std::string'
|
|
1689
|
+
elif fval in ('true', 'false'):
|
|
1690
|
+
ftype = 'bool'
|
|
1691
|
+
elif '.' in fval and fval.replace('.', '').replace('-', '').isdigit():
|
|
1692
|
+
ftype = 'double'
|
|
1693
|
+
elif fval.lstrip('-').isdigit():
|
|
1694
|
+
ftype = 'int'
|
|
1695
|
+
elif fval.startswith('{') or fval.startswith('std::vector'):
|
|
1696
|
+
ftype = 'std::vector<int>'
|
|
1697
|
+
else:
|
|
1698
|
+
ftype = 'int' # Default fallback
|
|
1699
|
+
else:
|
|
1700
|
+
ftype = 'int' # Default for uninitialized
|
|
1350
1701
|
lines.append(f' {ftype} {fname};')
|
|
1351
1702
|
|
|
1352
1703
|
if cls.fields:
|
|
@@ -1360,14 +1711,26 @@ class PythonToCppConverter:
|
|
|
1360
1711
|
params = self._format_params_with_const_ref(method.params)
|
|
1361
1712
|
static = 'static ' if method.is_static else ''
|
|
1362
1713
|
const = ' const' if method.is_const else ''
|
|
1363
|
-
|
|
1714
|
+
# Add template prefix if method uses generic containers
|
|
1715
|
+
if self._needs_template(method.params):
|
|
1716
|
+
lines.append(f' template<typename T>')
|
|
1717
|
+
ret_type = self._get_template_return_type(method.return_type, method.body, method.params)
|
|
1718
|
+
lines.append(f' {static}{ret_type} {method.name}({params}){const};')
|
|
1719
|
+
else:
|
|
1720
|
+
lines.append(f' {static}{method.return_type} {method.name}({params}){const};')
|
|
1364
1721
|
|
|
1365
1722
|
lines.append('};')
|
|
1366
1723
|
lines.append('')
|
|
1367
1724
|
|
|
1368
1725
|
for func in functions:
|
|
1369
1726
|
params = self._format_params_with_const_ref(func.params)
|
|
1370
|
-
|
|
1727
|
+
# Add template prefix if function uses generic containers
|
|
1728
|
+
if self._needs_template(func.params):
|
|
1729
|
+
lines.append('template<typename T>')
|
|
1730
|
+
ret_type = self._get_template_return_type(func.return_type, func.body, func.params)
|
|
1731
|
+
lines.append(f'{ret_type} {func.name}({params});')
|
|
1732
|
+
else:
|
|
1733
|
+
lines.append(f'{func.return_type} {func.name}({params});')
|
|
1371
1734
|
|
|
1372
1735
|
lines.append('')
|
|
1373
1736
|
lines.append('} // namespace includecpp')
|
|
@@ -1588,16 +1951,39 @@ class PythonToCppConverter:
|
|
|
1588
1951
|
for method in cls.methods:
|
|
1589
1952
|
params = self._format_params_with_const_ref(method.params)
|
|
1590
1953
|
const = ' const' if method.is_const else ''
|
|
1591
|
-
|
|
1954
|
+
# Add template prefix if method uses generic containers
|
|
1955
|
+
if self._needs_template(method.params):
|
|
1956
|
+
lines.append('template<typename T>')
|
|
1957
|
+
ret_type = self._get_template_return_type(method.return_type, method.body, method.params)
|
|
1958
|
+
lines.append(f'{ret_type} {cls.name}::{method.name}({params}){const} {{')
|
|
1959
|
+
else:
|
|
1960
|
+
lines.append(f'{method.return_type} {cls.name}::{method.name}({params}){const} {{')
|
|
1592
1961
|
lines.append(method.body)
|
|
1593
1962
|
lines.append('}')
|
|
1963
|
+
# Add explicit template instantiations for common types
|
|
1964
|
+
if self._needs_template(method.params):
|
|
1965
|
+
ret_type = self._get_template_return_type(method.return_type, method.body, method.params)
|
|
1966
|
+
inst_lines = self._generate_explicit_instantiation(
|
|
1967
|
+
method.name, method.params, ret_type, cls.name, const)
|
|
1968
|
+
lines.extend(inst_lines)
|
|
1594
1969
|
lines.append('')
|
|
1595
1970
|
|
|
1596
1971
|
for func in functions:
|
|
1597
1972
|
params = self._format_params_with_const_ref(func.params)
|
|
1598
|
-
|
|
1973
|
+
# Add template prefix if function uses generic containers
|
|
1974
|
+
if self._needs_template(func.params):
|
|
1975
|
+
lines.append('template<typename T>')
|
|
1976
|
+
ret_type = self._get_template_return_type(func.return_type, func.body, func.params)
|
|
1977
|
+
lines.append(f'{ret_type} {func.name}({params}) {{')
|
|
1978
|
+
else:
|
|
1979
|
+
lines.append(f'{func.return_type} {func.name}({params}) {{')
|
|
1599
1980
|
lines.append(func.body)
|
|
1600
1981
|
lines.append('}')
|
|
1982
|
+
# Add explicit template instantiations for common types
|
|
1983
|
+
if self._needs_template(func.params):
|
|
1984
|
+
ret_type = self._get_template_return_type(func.return_type, func.body, func.params)
|
|
1985
|
+
inst_lines = self._generate_explicit_instantiation(func.name, func.params, ret_type)
|
|
1986
|
+
lines.extend(inst_lines)
|
|
1601
1987
|
lines.append('')
|
|
1602
1988
|
|
|
1603
1989
|
lines.append('} // namespace includecpp')
|
|
@@ -1608,6 +1994,7 @@ class PythonToCppConverter:
|
|
|
1608
1994
|
class CppToPythonConverter:
|
|
1609
1995
|
def __init__(self):
|
|
1610
1996
|
self.indent = ' '
|
|
1997
|
+
self._current_class_fields = set() # Track fields for self. prefix
|
|
1611
1998
|
|
|
1612
1999
|
def convert(self, source: str, module_name: str) -> str:
|
|
1613
2000
|
"""Convert C++ source to Python."""
|
|
@@ -1737,8 +2124,9 @@ class CppToPythonConverter:
|
|
|
1737
2124
|
body = match.group(3)
|
|
1738
2125
|
|
|
1739
2126
|
fields = self._parse_class_fields(body)
|
|
1740
|
-
|
|
1741
|
-
|
|
2127
|
+
field_names = {f[0] for f in fields} # Extract field names for self. prefix
|
|
2128
|
+
methods = self._parse_class_methods(body, name, field_names)
|
|
2129
|
+
constructors = self._parse_constructors(body, name, field_names)
|
|
1742
2130
|
|
|
1743
2131
|
classes.append(ClassInfo(
|
|
1744
2132
|
name=name,
|
|
@@ -1748,6 +2136,9 @@ class CppToPythonConverter:
|
|
|
1748
2136
|
constructors=constructors
|
|
1749
2137
|
))
|
|
1750
2138
|
|
|
2139
|
+
# Clear class fields after processing this class
|
|
2140
|
+
self._current_class_fields = set()
|
|
2141
|
+
|
|
1751
2142
|
return classes
|
|
1752
2143
|
|
|
1753
2144
|
def _parse_structs(self, source: str) -> List[StructInfo]:
|
|
@@ -1832,9 +2223,13 @@ class CppToPythonConverter:
|
|
|
1832
2223
|
|
|
1833
2224
|
return fields
|
|
1834
2225
|
|
|
1835
|
-
def _parse_class_methods(self, body: str, class_name: str) -> List[FunctionInfo]:
|
|
2226
|
+
def _parse_class_methods(self, body: str, class_name: str, field_names: set = None) -> List[FunctionInfo]:
|
|
1836
2227
|
methods = []
|
|
1837
2228
|
|
|
2229
|
+
# Set current class fields for self. prefix during body conversion
|
|
2230
|
+
if field_names:
|
|
2231
|
+
self._current_class_fields = field_names
|
|
2232
|
+
|
|
1838
2233
|
pattern = r'(?:(static|virtual)\s+)?(\w+(?:<[^>]+>)?(?:\s*[*&])?)\s+(\w+)\s*\(([^)]*)\)\s*(const)?\s*(?:\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}|;)'
|
|
1839
2234
|
|
|
1840
2235
|
for match in re.finditer(pattern, body):
|
|
@@ -1863,9 +2258,13 @@ class CppToPythonConverter:
|
|
|
1863
2258
|
|
|
1864
2259
|
return methods
|
|
1865
2260
|
|
|
1866
|
-
def _parse_constructors(self, body: str, class_name: str) -> List[FunctionInfo]:
|
|
2261
|
+
def _parse_constructors(self, body: str, class_name: str, field_names: set = None) -> List[FunctionInfo]:
|
|
1867
2262
|
constructors = []
|
|
1868
2263
|
|
|
2264
|
+
# Set current class fields for self. prefix during body conversion
|
|
2265
|
+
if field_names:
|
|
2266
|
+
self._current_class_fields = field_names
|
|
2267
|
+
|
|
1869
2268
|
pattern = rf'{class_name}\s*\(([^)]*)\)\s*(?::\s*[^{{]+)?\s*\{{([^{{}}]*(?:\{{[^{{}}]*\}}[^{{}}]*)*)\}}'
|
|
1870
2269
|
|
|
1871
2270
|
for match in re.finditer(pattern, body):
|
|
@@ -2172,6 +2571,15 @@ class CppToPythonConverter:
|
|
|
2172
2571
|
expr = re.sub(r'std::min\(([^,]+),\s*([^)]+)\)', r'min(\1, \2)', expr)
|
|
2173
2572
|
expr = re.sub(r'std::max\(([^,]+),\s*([^)]+)\)', r'max(\1, \2)', expr)
|
|
2174
2573
|
|
|
2574
|
+
# Handle std::accumulate(container.begin(), container.end(), init) -> sum(container) [+ init]
|
|
2575
|
+
def _accumulate_to_sum(m):
|
|
2576
|
+
container = m.group(1)
|
|
2577
|
+
init_val = m.group(2).strip()
|
|
2578
|
+
if init_val == '0' or init_val == '0.0':
|
|
2579
|
+
return f'sum({container})'
|
|
2580
|
+
return f'sum({container}) + {init_val}'
|
|
2581
|
+
expr = re.sub(r'std::accumulate\((\w+)\.begin\(\),\s*\1\.end\(\),\s*([^)]+)\)', _accumulate_to_sum, expr)
|
|
2582
|
+
|
|
2175
2583
|
# Handle .size() -> len()
|
|
2176
2584
|
expr = re.sub(r'(\w+)\.size\(\)', r'len(\1)', expr)
|
|
2177
2585
|
|
|
@@ -2209,6 +2617,12 @@ class CppToPythonConverter:
|
|
|
2209
2617
|
# Handle negation !
|
|
2210
2618
|
expr = re.sub(r'!(\w)', r'not \1', expr)
|
|
2211
2619
|
|
|
2620
|
+
# Add self. prefix for class member access (when not already prefixed)
|
|
2621
|
+
if self._current_class_fields:
|
|
2622
|
+
for field in self._current_class_fields:
|
|
2623
|
+
# Match field name that's not already prefixed with self. or another identifier
|
|
2624
|
+
expr = re.sub(rf'(?<![.\w])(?<!self\.){re.escape(field)}(?=\s*[=,)\];\s]|$)', f'self.{field}', expr)
|
|
2625
|
+
|
|
2212
2626
|
return expr
|
|
2213
2627
|
|
|
2214
2628
|
def _generate_struct(self, struct: StructInfo) -> List[str]:
|