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.
@@ -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
- param_type = 'auto'
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
- expr_str = self._convert_expr(stmt.value)
661
- lines.append(f'{ind}{expr_str};')
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 likely a string already
1243
- if any(x in expr for x in ['\"', '.c_str()', 'std::string']):
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 _format_params_with_const_ref(self, params: List[Tuple[str, str]]) -> str:
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
- if ptype in ('std::string', 'std::vector', 'std::unordered_map', 'std::unordered_set') or ptype.startswith('std::'):
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, _ in cls.fields:
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
- lines.append(f' {static}{method.return_type} {method.name}({params}){const};')
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
- lines.append(f'{func.return_type} {func.name}({params});')
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
- lines.append(f'{method.return_type} {cls.name}::{method.name}({params}){const} {{')
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
- lines.append(f'{func.return_type} {func.name}({params}) {{')
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
- methods = self._parse_class_methods(body, name)
1741
- constructors = self._parse_constructors(body, name)
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]: