Skip to content

Instantly share code, notes, and snippets.

@katyo
Last active November 26, 2020 06:43
Show Gist options
  • Save katyo/1c2a39c490e360049245868f1aa76515 to your computer and use it in GitHub Desktop.
Save katyo/1c2a39c490e360049245868f1aa76515 to your computer and use it in GitHub Desktop.
import sys
import os
import re
from optparse import OptionParser
import ConfigParser
try:
import pygccxml
except ImportError:
print "You must instal pygccxml in-order to run this script."
sys.exit(1)
from pygccxml.declarations.cpptypes import *
from pygccxml.declarations.type_traits import *
from pygccxml.declarations.type_traits_classes import *
from pygccxml.declarations.calldef import *
from pygccxml.declarations.calldef_types import *
from pygccxml.declarations.free_calldef import *
from pygccxml.declarations.calldef_members import *
from pygccxml.declarations.class_declaration import ACCESS_TYPES, class_declaration_t
from pygccxml.declarations.typedef import typedef_t
class UnsupportedError(NotImplementedError):
"""Exception for unsupported features."""
pass
DEBUG = False
ARRAY_SIZE_VAR_NAME = 'arr_size'
RET_VAL_ON_EXCEPTION = 'NULL'
GENERATED_FILE_SUFFIX = "_C_Wrapper"
THIS_VAR_NAME = 'class_this'
RET_VAL_CLASS_NAME = 'ptr_ret_val_class'
WAS_EXCEPTION_ARG_NAME = 'ptr_was_exception'
C_BOOL_TYPE_NAME = 'BOOL_C'
C_TRUE_VAL = 'TRUE_C'
C_FALSE_VAL = 'FALSE_C'
# TODO: check whether those vars are in [arg_info.arg_name for arg_info in func_info.func_args_info_list], and if so - add a suffix.
PYGCCXML_DTOR_TOKEN = re.compile(r"._\d+")
PYGCCXML_FUNC_DECL_ARGS = re.compile(r".*\((.*?)\)") # =The last ()'s contents.
OPERATOR_MAP = {
'+': "plus",
'-': "minus",
'*': "multiply",
'/': "divison",
'%': "mod",
'^': "bitwise_xor",
'&': "bitwise_and",
'|': "bitwise_or",
'~': "bitwise_not",
'!': "not",
'=': "assign",
'<': "smaller",
'>': "bigger",
'+=': "plus_assign",
'-=': "minus_assign",
'*=': "multiply_assign",
'/=': "division_assign",
'%=': "mod_assign",
'^=': "bitwise_xor_assign",
'&=': "bitwise_and_assign",
'|=': "bitwise_or_assign",
'<<': "shift_left",
'>>': "shift_right",
'<<=': "shift_left_assign",
'>>=': "shift_right_assign",
'==': "equal",
'!=': "not_assign",
'<=': "smaller_or_equal",
'>=': "bigger_or_equal",
'&&': "and",
'||': "or",
'++': "plus_plus",
'--': "minus_minus",
',': "comma",
'->*': "pointer_redirect",
'->': "redirect",
'()': "function_call",
'[]': "subscript",
'new': "new",
'new []': "new_array",
'delete': "delete",
'delete []': "delete_array"
} # TODO: Check the function_call op.
OPERATORS_LIST_BY_LENGTH = sorted(OPERATOR_MAP.keys(), key=len, reverse=True) # From the longest to the shortest, to check '+=' before '+' (or '=').
# Proxy funcs to shield from possible pygccxml implementation changes:
def is_declarated_type(arg):
"""Returns whether the argument is a declaration type."""
return isinstance(arg, declarated_t)
def is_ellipsis(arg):
"""Returns whether the argument is ellipsis (...)."""
return isinstance(arg, ellipsis_t)
def is_qualifier(arg):
"""Returns whether the argument is a qualifier."""
return isinstance(arg, type_qualifiers_t)
def is_unknown_type(arg):
"""Returns whether the argument is an unknown type."""
return isinstance(arg, unknown_t)
def is_member_function_type(arg):
"""Returns whether the argument is a member function type."""
return isinstance(arg, member_function_type_t)
def is_typedef(arg):
"""Returns whether the argument is a typedef."""
return isinstance(arg, typedef_t)
def get_public_default_ctor(cls):
"""Returns the class public default ctor, or None if one doesn't exist."""
return has_trivial_constructor(cls)
def has_public_dtor(cls):
"""Returns whether the class has a public destructor."""
return has_public_destructor(cls)
def is_abstract_class_declaration(arg):
"""Returns whether arg is a class declaration that is not a typedef - for example: typedef abstract_class_decl typedef_decl"""
#return is_class_declaration(decl) and not is_typedef(decl)
return isinstance(arg, class_declaration_t)
def get_full_name(decl, sub_seq=None):
"""Generates a C++ token's name, including namespaces."""
#if is_abstract_class_declaration(decl):
# ns_name = get_full_name(decl.parent)
# if not ns_name.endswith("::"): # True to every namespace except for the global namespace.
# ns_name += "::"
# return ns_name + decl.declaration.name
full_name = pygccxml.declarations.full_name(decl)
if sub_seq is not None:
for old_token, new_token in sub_seq:
full_name = full_name.replace(old_token, new_token)
return full_name
def get_func_decl_str(func):
"""Return a func declaration string."""
args_str = PYGCCXML_FUNC_DECL_ARGS.match(func.create_decl_string()).group(1).strip()
return "%s(%s)" % (get_full_name(func), args_str)
def python_to_camel_case(decl_str):
"""Python to (Upper-)Camel Case naming convention, e.g.: "func_name" -> "FuncName" """
words = decl_str.split("_")
return "".join([word.capitalize() for word in words])
def strip_global_ns(decl_str):
"""Strip the global namespace ot a C++ declaration string."""
return decl_str.lstrip(':')
def get_c_name(decl, is_full_name=True, c_sub_seq=None):
"""Generates a C token from a C++ token."""
if is_full_name:
cpp_name = get_full_name(decl)
else:
cpp_name = decl.name
c_name = strip_global_ns(cpp_name).replace('::', '_').replace('~', "delete_").replace('>', '_').replace('<', '_').replace(' ', '').replace(',', '_').replace('*', "_ptr_").replace('&', "_ref_")
# The first substitution is for namespaces. The second is for dtors. All others are for templates.
if c_sub_seq is not None:
for old_token, new_token in c_sub_seq:
if old_token: # FIXME:
c_name = c_name.replace(old_token, new_token)
return c_name
def get_class_ptr_name(class_c_name):
"""Generates a class pointer token from its C class token."""
return "PTR_%s" % (class_c_name,)
def get_error_arg_str(is_c99):
"""Returns the type-name string of the error arg."""
if is_c99:
c_bool_type_str = "bool"
else:
c_bool_type_str = C_BOOL_TYPE_NAME
return "%s *%s" % (c_bool_type_str, WAS_EXCEPTION_ARG_NAME)
def get_enum_c_name(enum):
"""Generates a C enum token from a C++ one."""
enum_name = get_c_name(enum)
if enum_name == enum.name: # To prevent enum redifinition in the global scope. unlike typedef redifinition - it causes a compilation error.
enum_name += "_C" # TODO: Can (still) cause redifinition.
return enum_name
def get_header_guard_name(generated_base_file_path):
"""Returns the header guard. for a specific header file name."""
generated_header_file_name = os.path.basename(generated_base_file_path+".h")
return generated_header_file_name.upper().replace('.', '_')
def is_public_concrete_func(member_func):
"""Reurns whether a member function\operator is public and concrete."""
return ((member_func.access_type == ACCESS_TYPES.PUBLIC) and (member_func.virtuality != VIRTUALITY_TYPES.PURE_VIRTUAL))
def get_class_by_decl(global_ns, class_decl, is_safe=True):
"""Returns the class for a class declaration."""
try:
return global_ns.class_(get_full_name(class_decl))
except pygccxml.declarations.runtime_errors.multiple_declarations_found_t:
#print(global_ns, get_full_name(class_decl))
#pass
return global_ns.classes(get_full_name(class_decl))[1]
except pygccxml.declarations.runtime_errors.declaration_not_found_t:
if is_safe:
raise UnsupportedError("No concrete class for class declaration %s. Possible reason: template instatiation is missing." % (str(class_decl),))
else:
raise
def exception_handling_str(generate_exception_handling_code, output_str, generate_error_arg, is_c99, is_void_ret_type, ret_type_c_str):
"""Generates exception handling wrapper around an implementation output string, with or without an error argument."""
if not generate_exception_handling_code:
return output_str
if is_void_ret_type:
on_exception_return_str = ""
else:
on_exception_return_str = "\treturn (%s) %s;\n" % (ret_type_c_str, RET_VAL_ON_EXCEPTION)
if is_c99:
c_false_str = "false"
c_true_str = "true"
else:
c_false_str = C_FALSE_VAL
c_true_str = C_TRUE_VAL
error_arg_no_exception_str = ""
error_arg_was_exception_str = ""
if generate_error_arg:
error_arg_no_exception_str = "if((void *)%s != NULL) (*%s) = %s;\n" % (WAS_EXCEPTION_ARG_NAME, WAS_EXCEPTION_ARG_NAME, c_false_str)
error_arg_was_exception_str = "if((void *)%s != NULL) (*%s) = %s;\n" % (WAS_EXCEPTION_ARG_NAME, WAS_EXCEPTION_ARG_NAME, c_true_str)
return """ try {
%s%s
}
catch(...) {
%s\t%s
}""" % (error_arg_no_exception_str, output_str, error_arg_was_exception_str, on_exception_return_str)
class MemoryFile(object):
"""A class to implement a file on the memory."""
MARK_TOKEN = "@@@"
def __init__(self, path):
self.path = path
self.lines = [self.MARK_TOKEN]
def close(self):
"""Closes the memory file."""
self.lines.remove(self.MARK_TOKEN)
out_file = open(self.path, 'w')
out_file.write("\n".join(self.lines))
out_file.close()
def write(self, str):
"""Writes a string to the memory file."""
self.lines.append(str)
def write_at_mark(self, str):
"""Writes a string at the mark position of the memory file."""
self.lines.insert(self.lines.index(self.MARK_TOKEN), str)
def set_mark(self):
"""Set the mark posiion in the memory file."""
self.lines.remove(self.MARK_TOKEN)
self.lines.append(self.MARK_TOKEN)
class BasicOutputClass(object):
"""A class to handle the files output."""
def __init__(self, header_file_path, generate_dl):
self.header_file_path = header_file_path
header_file_name = os.path.basename(header_file_path)
generated_base_file_name = header_file_name[:header_file_name.rindex(".h")]+GENERATED_FILE_SUFFIX
self.generated_base_file_path = os.path.join(os.path.dirname(header_file_path), generated_base_file_name)
self.generate_dl = generate_dl
self.cpp_file = MemoryFile(self.generated_base_file_path+".cpp")
self.h_file = MemoryFile(self.generated_base_file_path+".h")
if generate_dl:
self.def_file = MemoryFile(self.generated_base_file_path+".def")
def close(self):
"""Forces the output file creation."""
self.cpp_file.close()
self.h_file.close()
if self.generate_dl:
self.def_file.close()
def write_to_cpp_file(self, output_str):
"""Write a string in the cpp file."""
self.cpp_file.write(output_str+'\n')
if DEBUG:
print "CPP: %s" % (output_str,)
def write_to_h_file(self, output_str):
"""Write a string in the header file."""
self.h_file.write(output_str+'\n')
if DEBUG:
print "H: %s" % (output_str,)
def write_at_h_file_mark(self, output_str):
"""Write a string in the header file's mark."""
self.h_file.write_at_mark(output_str+'\n')
if DEBUG:
print "H (FRONT): %s" % (output_str,)
def write_to_def_file(self, output_str):
"""Write a string in the def file, if this option is enabled."""
if self.generate_dl:
self.def_file.write(output_str+'\n')
if DEBUG:
print "DEF: %s" % (output_str,)
def mark_h_file_front(self):
"""Mark the start of the header file, where the typedefs would be written."""
self.h_file.set_mark()
class CodeOutputClass(BasicOutputClass):
"""A class to handle the code output."""
def __init__(self, header_file_path, generate_exception_handling_code, generate_dl, is_compact_string, generate_operators, is_assume_copy, is_assume_assign, generate_error_arg, is_verbose, is_camel_case):
super(CodeOutputClass, self).__init__(header_file_path, generate_dl)
self.generate_exception_handling_code = generate_exception_handling_code
self.is_compact_string = is_compact_string
self.generate_operators = generate_operators
self.is_assume_copy = is_assume_copy
self.is_assume_assign = is_assume_assign
self.generate_error_arg = generate_error_arg
self.is_verbose = is_verbose
self.is_camel_case = is_camel_case
def __del__(self):
super(CodeOutputClass, self).__del__()
def output_class_typedef(self, cls, global_context, alternate_class_c_ptr_name=None):
"""Outputs a C class representation."""
class_c_name, class_c_ptr_name = global_context.add_class(cls, alternate_class_c_ptr_name)
verbose_str = ""
if self.is_verbose:
verbose_str = "\t/* A C wrapper for class %s */" % (get_full_name(cls),)
self.write_at_h_file_mark("typedef struct _%s *%s;%s" % (class_c_name, class_c_ptr_name, verbose_str))
def output_func(self, func, global_context, is_array_version=False, generate_min_args_ver_only=False):
"""Outputs a function C wrapper."""
func_info = FuncInfo(func, global_context, self.generate_error_arg, generate_min_args_ver_only)
c_func_name = func_info.c_func_name
if (func_info.func_type == func_info.DTOR):
# To fix the pygccxml dtor name of ~._<num> for declarated classes.
c_func_name = PYGCCXML_DTOR_TOKEN.sub(get_c_name(func.parent, False), c_func_name)
for used_def_vals_num in range(func_info.optional_args_num+1): # Variations to handle default values.
func_args_decl_list = [arg_c_str for arg_c_str in func_info.gen_func_args_c_strs()]
func_args = func_args_decl_list[:len(func_args_decl_list)-used_def_vals_num]
if is_array_version and (func_info.func_type == func_info.CTOR):
func_args.append("size_t %s" % (ARRAY_SIZE_VAR_NAME,)) # Needed for new[].
func_args_str = ", ".join(func_args)
func_args_impl_list = []
for arg_info in func_info.func_args_info_list[:len(func_info.func_args_info_list)-used_def_vals_num]:
redirection_str = ""
if arg_info.is_redirected:
redirection_str = "*"
cast_str = ""
if arg_info.cast_str:
cast_str = "(%s)" % (arg_info.cast_str,)
arg_name = arg_info.arg_name
func_args_impl_list.append((redirection_str, cast_str, arg_name))
func_args_impl_str = ", ".join(["%s%s%s" % (redirection_str, cast_str, arg_name) for redirection_str, cast_str, arg_name in func_args_impl_list])
if is_array_version:
c_func_name = "%s_array" % (c_func_name,)
unique_c_func_name = global_context.generate_unique_token(c_func_name)
if self.is_camel_case:
unique_c_func_name = python_to_camel_case(unique_c_func_name)
self.write_to_def_file('\t'+unique_c_func_name)
func_prototype = "%s %s(%s)" % (func_info.ret_type_info.arg_type_c_str, unique_c_func_name , func_args_str)
verbose_str = ""
if self.is_verbose:
verbose_str = "\t/* A C wrapper for func %s */" % (get_func_decl_str(func),)
self.write_to_h_file("%s;%s" % (func_prototype, verbose_str))
self.write_to_cpp_file("%s {" % (func_prototype,))
ret_val_c_str = "%s%s(%s)" % (func_info.class_redirection, func_info.full_name, func_args_impl_str)
output_str = "\t\t"
if (func_info.func_type == func_info.CTOR):
if func_info.is_default_ctor():
ctor_args_str = ""
else:
ctor_args_str = "(%s)" % (func_args_impl_str,)
array_str = ""
if is_array_version:
array_str = "[%s]" % (ARRAY_SIZE_VAR_NAME,)
nothrow_str = ""
if not self.generate_exception_handling_code:
nothrow_str = "(std::nothrow) "
ret_val_c_str = "new %s%s%s%s" % (nothrow_str, func_info.ret_type_info.class_name, ctor_args_str, array_str)
elif (func_info.func_type == func_info.DTOR):
array_str = ""
if is_array_version:
array_str = "[]"
cast_str = "(%s*)" % (global_context.get_full_name(func.parent),)
ret_val_c_str = "delete %s(%s%s)" % (array_str, cast_str, THIS_VAR_NAME)
else:
if (func_info.ret_type_info.is_class) and (func_info.ret_type_info.is_redirected):# and not(func_info.ret_type_info.is_ptr):
# Special case: a class should be returned by value. We'll try to return a pointer to a new instance, if possible.
nothrow_str = ""
if not self.generate_exception_handling_code:
nothrow_str = "(std::nothrow) "
if func_info.ret_type_info.can_create_with_copy_constructor or ((func_info.ret_type_info.can_create_with_copy_constructor is None) and self.is_assume_copy):
ret_val_c_str = "new %s%s(%s)" % (nothrow_str, func_info.ret_type_info.class_name, ret_val_c_str)
elif func_info.ret_type_info.can_create_with_default_constructor or ((func_info.ret_type_info.can_create_with_default_constructor is None) and self.is_assume_assign):
output_str += "%s *%s = new %s%s;\n" % (func_info.ret_type_info.class_name, RET_VAL_CLASS_NAME, nothrow_str, func_info.ret_type_info.class_name)
nothrow_null_check_str = ""
if not self.generate_exception_handling_code:
nothrow_null_check_str = "if((void*)%s != NULL) " % (RET_VAL_CLASS_NAME,)
output_str += "%s*%s = %s;\n" % (nothrow_null_check_str, RET_VAL_CLASS_NAME, ret_val_c_str)
ret_val_c_str = RET_VAL_CLASS_NAME
else:
raise UnsupportedError("Cannot handle a class return value without a public copy ctor or public default ctor an assignment operator in %s" % (func_info.full_name, ))
if func_info.ret_type_info.is_void():
return_str = ""
else:
return_str = "return "
cast_str = ""
if func_info.ret_type_info.cast_str:
cast_str = "(%s)" % (func_info.ret_type_info.arg_type_c_str,)
ref_str = ""
if (func_info.ret_type_info.is_ref and not(func_info.ret_type_info.is_class)):
ref_str = "&"
output_str += "%s%s%s%s;" % (return_str, cast_str, ref_str, ret_val_c_str)
output_str = exception_handling_str(self.generate_exception_handling_code, output_str, self.generate_error_arg, global_context.is_c99, func_info.ret_type_info.is_void(), func_info.ret_type_info.arg_type_c_str)
self.write_to_cpp_file("%s" % (output_str,))
self.write_to_cpp_file( "}")
def output_std_string(self, string_typedef, global_context):
"""Outputs the std::(w)string C wrapper."""
is_wstring, string_class, string_c_ptr_name = global_context.add_std_string(string_typedef)
if self.is_compact_string: # Otherwise, it would be outputed like other classes, recursively, as needed.
self.output_class_typedef(string_class, global_context, string_c_ptr_name)
public_default_ctor = get_public_default_ctor(string_class)
self.output_func(public_default_ctor, global_context)
self.output_func(public_default_ctor, global_context, True)
if is_wstring:
char_str = "wchar_t"
else:
char_str = "char"
char_ptr_str = "const %s *" % (char_str,)
for ctor in string_class.constructors():
if (len(ctor.required_args) == 1) and (char_ptr_str == ctor.required_args[0].decl_type.build_decl_string()):
self.output_func(ctor, global_context, False, True) # Outputs the std::(w)string(char*) ctor. We don't generate the allocator<char_str> optional arg.
break
string_dtor = has_destructor(string_class)
self.output_func(string_dtor, global_context)
self.output_func(string_dtor, global_context, True)
c_str_func = string_class.member_function("c_str")
self.output_func(c_str_func, global_context)
def output_enum(self, enum, global_context):
"""Outputs an enum C version."""
enum_name = get_enum_c_name(enum)
verbose_str = ""
if self.is_verbose:
verbose_str = "\t/* A C wrapper for enum %s */" % (get_full_name(enum),)
self.write_at_h_file_mark("enum %s {" % (enum_name,))
self.write_at_h_file_mark("%s" % (",\n".join(["\t%s=%s" % (global_context.generate_unique_token(key, True), val) for key, val in enum.values]),)) # Needed to handle 2 enums with same keys in different namespaces.
self.write_at_h_file_mark("};%s" % (verbose_str,))
global_context.add_enum(enum)
def output_typedef(self, typedef, global_context):
"""Outputs a typedef C version."""
typedef_info=ArgInfo(typedef.decl_type, global_context, get_c_name(typedef))
if typedef_info.is_c_decl: # There is no support for typedefs that aren't C declerations - they'll be stripped to their raw C-type.
verbose_str = ""
if self.is_verbose:
verbose_str = "\t/* A C wrapper for typedef %s */" % (get_full_name(typedef),)
self.write_at_h_file_mark("typedef %s;%s" % (typedef_info.get_type_name_str(), verbose_str))
global_context.add_typedef(typedef)
# TODO: What about a typedef of an enum?
def output_class_code(self, cls, global_context):
"""Output cls class code."""
for ctor in cls.constructors(allow_empty=True):
if (ctor.access_type == ACCESS_TYPES.PUBLIC):
self.output_func(ctor, global_context)
if has_public_dtor(cls):
dtor = has_destructor(cls)
self.output_func(dtor, global_context)
public_default_ctor = get_public_default_ctor(cls)
if public_default_ctor is not None:
self.output_func(public_default_ctor, global_context, True)
self.output_func(dtor, global_context, True)
for member_func in cls.member_functions(allow_empty=True):
if is_public_concrete_func(member_func):
self.output_func(member_func, global_context)
if self.generate_operators:
for member_op in cls.member_operators(allow_empty=True):
if is_public_concrete_func(member_op):
self.output_func(member_op, global_context)
def output_prefix_code(self, is_c99):
"""Output the prefix code (includes, base typedefs, etc.)."""
header_file_name = os.path.basename(self.header_file_path)
generated_header_file_name = os.path.basename(self.generated_base_file_path+".h")
generated_base_file_name = os.path.basename(self.generated_base_file_path)
self.write_to_cpp_file("""#include "%s" """ % (header_file_name,))
self.write_to_cpp_file("""#include "%s" """ % (generated_header_file_name,))
self.write_to_def_file("""LIBRARY "%s"
EXPORTS""" % (generated_base_file_name,))
header_guard = get_header_guard_name(self.generated_base_file_path)
self.write_to_h_file("#ifndef %s" % (header_guard,))
self.write_to_h_file("#define %s" % (header_guard,))
self.write_to_h_file("""#ifdef __cplusplus
extern "C" {
#endif""")
if not is_c99:
self.write_to_h_file("#define %s 0" % (C_FALSE_VAL,))
self.write_to_h_file("#define %s 1" % (C_TRUE_VAL,))
self.write_to_h_file("typedef unsigned char %s;" % (C_BOOL_TYPE_NAME,))
self.mark_h_file_front()
if self.generate_dl:
self.write_to_cpp_file("""#ifdef WIN32
#include <Windows.h>
extern "C" BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpReserved ) // reserved
{
// Perform actions based on the reason for calling.
switch( fdwReason )
{
case DLL_PROCESS_ATTACH:
// Initialize once for each new process.
// Return FALSE to fail DLL load.
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_PROCESS_DETACH:
// Perform any necessary cleanup.
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
#endif // WIN32""")
def output_suffix_code(self):
"""Output the suffix code (header protectors, etc.)."""
self.write_to_h_file("""#ifdef __cplusplus
}
#endif /* __cplusplus */""")
generated_header_file_name = os.path.basename(self.generated_base_file_path+".h")
self.write_to_h_file("#endif /* %s */" % (get_header_guard_name(self.generated_base_file_path),))
class CodeContext(object):
"""The code context (classes, typdefs, etc.) of the parsed header files."""
CLASS = 0
TYPEDEF = 1
ENUM = 2
def __init__(self, header_file_path, gccxml_file_path, compiler_type, is_c99):
generator_path, generator_name = pygccxml.utils.find_xml_generator()
#config = pygccxml.parser.config_t(gccxml_path=gccxml_file_path, compiler=compiler_type)
config = pygccxml.parser.xml_generator_configuration_t(
gccxml_path=gccxml_file_path, compiler=compiler_type,
xml_generator=generator_name, xml_generator_path=generator_path,
include_paths=["/usr/include", "/usr/include/opencascade"])
#config = pygccxml.parser.xml_generator_configuration_t(
# xml_generator_path=gccxml_file_path,
# xml_generator=compiler_type or "gccxml")
decls = pygccxml.parser.parse([header_file_path], config)
self.global_ns = pygccxml.declarations.get_global_namespace(decls)
self.is_c99 = is_c99
self.class_c_ptrs_map = {}
self.token_freqs = {}
self.typedefs_map = {}
self.std_wstring_c_name = None
self.std_string_c_name = None
self.recursive_classes = []
self.recursive_typedefs = []
self.recursive_enums = []
self.enums_map = {}
def __getattr__(self, attr_name):
""" CodeContext becomes a proxy to the global namespace attributes."""
return getattr(self.global_ns, attr_name)
def generate_unique_token(self, c_func_name, always_add_suffix=False):
"""Generates a unique C token."""
# TODO: Use the args types instead, perhaps.
if c_func_name in self.token_freqs:
self.token_freqs[c_func_name] += 1
return "%s%d" % (c_func_name, self.token_freqs[c_func_name])
else:
self.token_freqs[c_func_name] = 1
if always_add_suffix:
return "%s%d" % (c_func_name, self.token_freqs[c_func_name])
else:
return c_func_name
def add_class(self, cls, alternate_class_c_ptr_name=None):
"""Add cls to the code context."""
class_c_name = self.get_c_name(cls)
if alternate_class_c_ptr_name is None:
class_c_ptr_name = get_class_ptr_name(class_c_name)
else:
class_c_ptr_name = alternate_class_c_ptr_name
self.class_c_ptrs_map[self.get_full_name(cls)] = class_c_ptr_name
return (class_c_name, class_c_ptr_name)
def add_typedef(self, typedef):
"""Add typedef to the code context."""
self.typedefs_map[get_full_name(typedef)] = get_c_name(typedef)
def add_enum(self, enum):
"""Add an enum to the code context."""
self.enums_map[get_full_name(enum)] = get_enum_c_name(enum)
def get_class_data(self, cls):
"""Get the class data for cls from the code context."""
class_name = self.get_full_name(cls)
if class_name not in self.class_c_ptrs_map:
self.add_class(cls)
if is_abstract_class_declaration(cls):
cls = get_class_by_decl(self.global_ns, cls)
if cls is not None:
self.recursive_classes.append(cls)
return (class_name, self.class_c_ptrs_map[class_name])
def get_typedef_data(self, typedef):
"""Get the data for typedef from the code context."""
typedef_name = get_full_name(typedef)
if typedef_name not in self.typedefs_map:
self.add_typedef(typedef)
self.recursive_typedefs.append(typedef)
return (typedef_name, self.typedefs_map[typedef_name])
def get_enum_data(self, enum):
"""Get the data for enum from the code context."""
enum_name = get_full_name(enum)
if enum_name not in self.enums_map:
self.add_enum(enum)
self.recursive_enums.append(enum)
return (enum_name, self.enums_map[enum_name])
def add_std_string(self, string_typedef):
"""Add std::(w)string to the code context."""
string_class = get_class_by_decl(self.global_ns, string_typedef.decl_type.declaration, False)
string_name = string_typedef.name
is_wstring = ("wstring" in string_name)
if is_wstring:
self.wstring_ctor_name = get_public_default_ctor(string_class).name
self.std_wstring_c_name = get_c_name(string_class)
self.std_wstring_typedef_c_name = get_c_name(string_typedef)
self.wstring_name = string_name
self.wstring_full_name = get_full_name(string_class)
self.wstring_full_typedef_name = get_full_name(string_typedef)
string_c_ptr_name=get_class_ptr_name(self.std_wstring_typedef_c_name) # We replace the ptr of the class with that of the typedef - more readable.
else:
self.string_ctor_name = get_public_default_ctor(string_class).name
self.std_string_c_name = get_c_name(string_class)
self.std_string_typedef_c_name = get_c_name(string_typedef)
self.string_name = string_name
self.string_full_name = get_full_name(string_class)
self.string_full_typedef_name = get_full_name(string_typedef)
string_c_ptr_name=get_class_ptr_name(self.std_string_typedef_c_name) # We replace the ptr of the class with that of the typedef - more readable.
self.add_typedef(string_typedef)
return (is_wstring, string_class, string_c_ptr_name)
def gen_recursive_elems(self):
"""Generates the elements gathered recursively that needs to be outputed."""
while self.recursive_classes or self.recursive_typedefs or self.recursive_enums:
# Not a for loop - since classes may get into self.recursive_classes during this loop (e.g.: std::string::allocator inside std::string methods). The same is true for typedefs.
if self.recursive_enums:
yield (self.ENUM, self.recursive_enums.pop(0))
elif self.recursive_typedefs:
yield (self.TYPEDEF, self.recursive_typedefs.pop(0))
elif self.recursive_classes:
yield (self.CLASS, self.recursive_classes.pop(0))
def gen_sub_seq(self):
"""Generates a substitution sequence for C++ tokens."""
if self.std_wstring_c_name is not None:
yield (self.wstring_full_name, self.wstring_full_typedef_name) # Make the std::wstring declerations more readable.
if self.std_string_c_name is not None:
yield (self.string_full_name, self.string_full_typedef_name) # Make the std::wstring declerations more readable.
def gen_c_sub_seq(self):
"""Generates a substitution sequence for C tokens."""
if self.std_wstring_c_name is not None:
yield (self.std_wstring_c_name, self.std_wstring_typedef_c_name) # Make the std::wstring declerations more readable.
yield (self.wstring_ctor_name, self.wstring_name) # Replace basic_string() with wstring().
if self.std_string_c_name is not None:
yield (self.std_string_c_name, self.std_string_typedef_c_name) # Make the std::string declerations more readable.
yield (self.string_ctor_name, self.string_name) # Replace basic_string() with string().
def get_full_name(self, decl):
""" Returns decl full name in the current code context."""
return get_full_name(decl, self.gen_sub_seq())
def get_c_name(self, decl):
""" Returns decl C-name in the current code context."""
return get_c_name(decl, True, self.gen_c_sub_seq())
class ArgInfo(object):
"""A class to parse a func argument."""
def __init__(self, arg_type, global_context, arg_name=""):
self.class_name = None
self.is_const = False
self.is_ref = False
self.is_class = False
self.arg_type_c_str = None
self.is_ptr = False
self.is_enum = False
self.is_typedef = False
self.arg_type = arg_type
self.arg_name = arg_name
self.cast_str = ""
self.is_func_ptr = False
ptrs_list=[]
if "std::_Aux_cont" in str(arg_type):
# Handle special case for MSVSC 2008, for example, when trying to instantiate vector<int>.
# TODO: Handle the special case in a generic way.
raise UnsupportedError("No support for std::_Aux_cont in: %s" % (str(arg_type),))
curr_arg = arg_type
while not(is_fundamental(curr_arg)) or is_typedef(curr_arg) or is_declarated_type(curr_arg) or is_enum(curr_arg):
if is_abstract_class_declaration(curr_arg):
self.is_class = True
cls = get_class_by_decl(global_context.global_ns, curr_arg)
break
elif is_typedef(curr_arg):
self.is_typedef = True
typedef_name, typedef_c_name = global_context.get_typedef_data(curr_arg)
curr_arg = curr_arg.decl_type # Don't use remove_alias() since this deletes ALL the typedefs - and we don't want that.
#TODO: Is this good to classes, either?
elif is_declarated_type(curr_arg):
#curr_arg_decl_str = curr_arg.build_decl_string()
#if pygccxml.declarations.templates.is_instantiation(curr_arg_decl_str) and not(is_std_string(curr_arg) or is_std_wstring(curr_arg)):
# raise UnsupportedError("Currently templates are not supported.")
# TODO: Check STL contains, for example vector.
#self.is_typedef, typedef_name, typedef_c_name = global_context.get_possible_typedef_data(curr_arg)
curr_arg = curr_arg.declaration # And not remove_declarated(curr_arg) - since this would break a typedef completely, etc.
elif is_const(curr_arg):
self.is_const = True
curr_arg = remove_const(curr_arg)
elif is_ellipsis(curr_arg):
raise UnsupportedError("Ellipsis arg types are not handeled.")
elif is_qualifier(curr_arg):
curr_arg = base_type(curr_arg) # TODO: Is this correct?
elif is_unknown_type(curr_arg):
raise UnsupportedError("Unknown arg type: %s" % (str(curr_arg),))
elif is_volatile(curr_arg):
curr_arg = remove_volatile(curr_arg) # Not being dealt.
elif is_calldef_pointer(curr_arg):
curr_arg = curr_arg.base # remove_pointer() doesn't work here.
if is_member_function_type(curr_arg):
raise UnsupportedError("Member function pointers are not supported: %s" % (str(arg_type),))
self.is_func_ptr = True
self.func_ptr_info = FuncPtrInfo(curr_arg, arg_name, global_context)
break
elif is_pointer(curr_arg) or is_array(curr_arg):
ptrs_list.append(self.is_const)
self.is_const = False
curr_arg = remove_pointer(curr_arg)
elif is_reference(curr_arg):
self.is_ref = True # Only the 1st ref is handeled
curr_arg = remove_reference(curr_arg)
elif is_enum(curr_arg):
self.is_enum = True
break
elif is_class(curr_arg):
self.is_class = True
cls = curr_arg
break
self.is_c_bool = is_bool(curr_arg) and not global_context.is_c99 # This is redundent if the compiler is C99 compatible.
self.is_c_decl = not(self.is_class or self.is_ref or self.is_c_bool) #= A simple C decleration.
self.is_redirected = (self.is_class or self.is_ref)
if self.is_c_decl:
self.arg_type_c_str = strip_global_ns(arg_type.build_decl_string())
if self.is_typedef:
stripped_typedef_name = strip_global_ns(typedef_name)
if typedef_c_name != stripped_typedef_name: # There is a namespace
self.cast_str = self.arg_type_c_str
self.arg_type_c_str = self.arg_type_c_str.replace(typedef_name, typedef_c_name)
# The following line is needed to handle the case where arg_type.build_decl_string() is a typedef -> we've stripped the global namespace, and the type won't be replaced with the last line.
self.arg_type_c_str = self.arg_type_c_str.replace(stripped_typedef_name, typedef_c_name)
elif self.is_enum:
self.cast_str = self.arg_type_c_str
enum_name, enum_c_name = global_context.get_enum_data(curr_arg)
if "enum " not in enum_c_name: # C++ style's enum
enum_c_name = "enum %s" % (enum_c_name,)
self.arg_type_c_str = self.arg_type_c_str.replace(strip_global_ns(get_full_name(curr_arg)), enum_c_name)
else:
if self.is_class:
if cls is not None:
self.can_create_with_copy_constructor = has_copy_constructor(cls)
self.can_create_with_default_constructor = (has_public_assign(cls) and has_trivial_constructor(cls))
self.class_name, base_arg_type_c_str = global_context.get_class_data(cls)
else: # For class declaration with no concrete classes.
self.can_create_with_copy_constructor = None
self.can_create_with_default_constructor = None
self.class_name, base_arg_type_c_str = global_context.get_class_data(curr_arg)
if len(ptrs_list) > 0:
if ptrs_list.pop(): # The class ptr covers 1 redirection level...
base_arg_type_c_str = "const " + base_arg_type_c_str #...but we shouldn't forget the const correctness for the ptr we've just removed.
self.is_redirected = False
else:
base_arg_type_c_str = strip_global_ns(curr_arg.build_decl_string())
if self.is_ref: # The (is_class and is_ref) case is dealt in the is_class case.
ptrs_list.append(True) # We add a const indirection. Notice that self.is_const is about the contents - not about the ptr.
curr_ptrs_list = []
for is_ptr_const in reversed(ptrs_list): # reversed - since ptrs are read from right-to-left.
if is_ptr_const:
curr_ptrs_list.append('* const')
else:
curr_ptrs_list.append('*')
ptrs_str = ''.join(curr_ptrs_list)
const_content_str = ""
if self.is_const:
const_content_str = "const "
self.arg_type_c_str = "%s%s%s" % (const_content_str, base_arg_type_c_str, ptrs_str)
if self.is_class:
self.cast_str = self.arg_type_c_str.replace(global_context.get_class_data(curr_arg)[1], self.class_name+'*')
if self.is_c_bool:
self.cast_str = self.arg_type_c_str
self.arg_type_c_str = self.arg_type_c_str.replace("bool", C_BOOL_TYPE_NAME)
if self.is_func_ptr and not(self.is_typedef): # TODO: Is this position covers all bases?
self.arg_type_c_str = self.func_ptr_info.type_str
if len(ptrs_list) > 0:
self.is_ptr = True
def is_void(self):
"""Returns whether the argument is void."""
return is_void(self.arg_type)
def get_type_name_str(self):
"""Returns the type and name argument string."""
if self.is_func_ptr and not(self.is_typedef): # TODO: What about if the typedef is stripped down?
return self.arg_type_c_str.replace("(*)", "(*%s)" % (self.arg_name,))
return "%s %s" % (self.arg_type_c_str, self.arg_name)
class FuncPtrInfo:
"""A class to parse a function pointer."""
def __init__(self, func_type, name, global_context):
self.func_args_info_list = []
self.func_type = func_type
self.name = name
for arg in func_type.arguments_types:
self.func_args_info_list.append(ArgInfo(arg, global_context))
self.ret_type_info = ArgInfo(func_type.return_type, global_context)
func_args_decl_list = [arg_info.arg_type_c_str for arg_info in self.func_args_info_list]
func_args_str = ", ".join(func_args_decl_list)
self.type_str = "%s (*%s)(%s)" % (self.ret_type_info.arg_type_c_str, name, func_args_str)
class FuncInfo:
"""A class to parse a function or a class method."""
FREE_FUNC = 0
FREE_OP = 1
MEMBER_FUNC = 2
MEMBER_OP = 3
CTOR = 4
DTOR = 5
FUNC_TYPE_MAP = {free_function_t: FREE_FUNC,
free_operator_t: FREE_OP,
member_function_t: MEMBER_FUNC,
member_operator_t: MEMBER_OP,
constructor_t: CTOR,
destructor_t: DTOR}
def __init__(self, func, global_context, generate_error_arg, generate_min_args_ver_only=False):
if func.has_ellipsis:
raise UnsupportedError("Ellipsis arg types are not handeled.")
self.func_args_info_list = []
self.func = func
self.full_name = global_context.get_full_name(func)
self.c_func_name = global_context.get_c_name(func)
self.func_type = self.FUNC_TYPE_MAP[type(func)]
if (self.func_type == self.MEMBER_OP) or (self.func_type == self.FREE_OP):
if not("operator_" in self.c_func_name):
# Fix missing space in the func name ("operator+" -> "operator_+").
self.c_func_name = self.c_func_name.replace("operator", "operator_")
for op_name in OPERATORS_LIST_BY_LENGTH:
self.c_func_name = self.c_func_name.replace(op_name, OPERATOR_MAP[op_name])
self.generate_error_arg = generate_error_arg
self.is_c99 = global_context.is_c99
if generate_min_args_ver_only:
self.optional_args_num = 0
else:
self.optional_args_num = len(func.optional_args) # There are always len(func.optional_args) optional arguments, but we don't want to use them when we generate_min_args_ver_only.
if generate_min_args_ver_only:
arguments = func.required_args
else:
arguments = func.arguments
for arg in arguments:
self.func_args_info_list.append(ArgInfo(arg.decl_type, global_context, arg.name))
self.class_redirection = ""
if (self.func_type == self.MEMBER_FUNC) or (self.func_type == self.DTOR) or (self.func_type == self.MEMBER_OP):
self.class_name, class_c_ptr = global_context.get_class_data(func.parent)
self.class_arg_c_str = "%s %s" % (class_c_ptr, THIS_VAR_NAME)
if (self.func_type == self.MEMBER_FUNC) or (self.func_type == self.MEMBER_OP):
self.is_static = func.has_static
if self.is_static:
self.c_func_name += "_static"
self.class_redirection = ""
const_class_redirection_str = ""
if func.has_const:
self.class_arg_c_str = "const " + self.class_arg_c_str
self.c_func_name += "_const"
const_class_redirection_str = "const "
if not self.is_static:
self.class_redirection = "((%s%s*) %s)->" % (const_class_redirection_str, self.class_name, THIS_VAR_NAME,)
if self.func_type == self.CTOR:
self.ret_type_info = ArgInfo(func.parent, global_context)
# TODO: The ctor returns a ptr to the class. Here we put the class_t and not pointer_t(class_t) - since it messes-up the ArgInfo's ctor.
# The reason it works is that handling class and class-ptr is the same - but depending on it is very bad.
elif (self.func_type == self.DTOR):
self.ret_type_info = ArgInfo(void_t(), global_context)
else:
self.ret_type_info = ArgInfo(func.return_type, global_context)
def gen_func_args_c_strs(self):
"""A generator to the func args types and names."""
if self.generate_error_arg:
yield get_error_arg_str(self.is_c99)
if (self.func_type == self.DTOR) or (self.func_type == self.MEMBER_OP) or ((self.func_type == self.MEMBER_FUNC) and not self.is_static):
yield self.class_arg_c_str
for arg_info in self.func_args_info_list:
yield arg_info.get_type_name_str()
def is_default_ctor(self):
"""Returns whether the func is a default ctor."""
if self.func_type == self.CTOR:
return is_trivial_constructor(self.func)
return False
def unsupported_wrapper(func, ignore_unsupported_features=True):
"""A decorator to ignore unsupported features exception, if enabled."""
def decorated(*args, **kws):
try:
func(*args, **kws)
except UnsupportedError, ex:
if ignore_unsupported_features:
print ex.args[0]
else:
raise
return decorated
def generate_c_wrapper(header_file_path, generate_dl=True, generate_exception_handling_code=True, is_compact_string=True, is_assume_copy=False, is_assume_assign=False, generate_error_arg=True, is_verbose=False, generate_operators=True, ignore_unsupported_features=True, is_c99=False, is_camel_case=False, gccxml_file_path = '', compiler_type = None):
"""Outputs a C wrapper for header_file_path."""
if generate_error_arg and not(generate_exception_handling_code):
print "Ignoring error argument generation option, since the exception handling generation option is disabled"
generate_error_arg = False
header_file_path=os.path.abspath(header_file_path)
output_class = CodeOutputClass(header_file_path, generate_exception_handling_code, generate_dl, is_compact_string, generate_operators, is_assume_copy, is_assume_assign, generate_error_arg, is_verbose, is_camel_case)
global_context = CodeContext(header_file_path, gccxml_file_path, compiler_type, is_c99)
output_class.output_prefix_code(global_context.is_c99)
output_class.output_typedef=unsupported_wrapper(output_class.output_typedef, ignore_unsupported_features)
output_class.output_func=unsupported_wrapper(output_class.output_func, ignore_unsupported_features)
try:
string_typedef = global_context.typedef(name="::std::string")
output_class.output_std_string(string_typedef, global_context)
string_typedef = global_context.typedef(name="::std::wstring")
output_class.output_std_string(string_typedef, global_context)
except pygccxml.declarations.runtime_errors.declaration_not_found_t:
pass
for cls in global_context.classes(header_file=header_file_path, allow_empty=True):
output_class.output_class_typedef(cls, global_context)
for typedef in global_context.typedefs(header_file=header_file_path, allow_empty=True):
output_class.output_typedef(typedef, global_context)
for enum in global_context.enumerations(header_file=header_file_path, allow_empty=True):
output_class.output_enum(enum, global_context)
for cls in global_context.classes(header_file=header_file_path, allow_empty=True):
output_class.output_class_code(cls, global_context)
for free_func in global_context.free_functions(header_file=header_file_path, allow_empty=True):
output_class.output_func(free_func, global_context)
if output_class.generate_operators:
for free_op in global_context.free_operators(header_file=header_file_path, allow_empty=True):
output_class.output_func(free_op, global_context)
for elem_type, elem in global_context.gen_recursive_elems():
if elem_type == global_context.CLASS:
output_class.output_class_typedef(elem, global_context)
output_class.output_class_code(elem, global_context)
elif elem_type == global_context.TYPEDEF:
output_class.output_typedef(elem, global_context)
elif elem_type == global_context.ENUM:
output_class.output_enum(elem, global_context)
output_class.output_suffix_code()
output_class.close() # Forces the output file creation.
def safe_config_get_wrapper(config_get_func):
"""A decorator to mask ConfigParser methods from option not existing exception, returning a default value instead."""
def decorated(section_name, key_name, def_val=None):
try:
return config_get_func(section_name, key_name)
except ConfigParser.NoOptionError:
return def_val
return decorated
def get_option(options, option, def_val):
"""Returns a default value (other than None) when an optparse var option does not exist."""
ret_val = getattr(options, option)
if ret_val is None:
return def_val
return ret_val
def parse_option(config_get_func, section_name, option_name, options, def_val):
"""Parses one program option. The args parsing order is:
1. Command line args.
2. Config file options.
3. Default values."""
option = def_val
if config_get_func is not None:
option = config_get_func(section_name, option_name, def_val)
return get_option(options, option_name, option)
if __name__ == '__main__':
cmd_line_parser = OptionParser("usage: %prog [options] <header_file_path>")
cmd_line_parser.add_option("-g", "--gccxml", dest='gccxml_file_path', help="The gccxml file path")
cmd_line_parser.add_option("-c", "--config", dest='config_file_path', help="The config file path")
cmd_line_parser.add_option("-t", "--compiler", dest='compiler_type', help="The compiler type")
cmd_line_parser.add_option("-i", "--ignore", action='store_false', dest='ignore_unsupported_features', help="Raise exception if an unsupported feature (like templates) is encountered.")
cmd_line_parser.add_option("-d", "--dl", action='store_false', dest='generate_dl', help="Don't generate a def file (and a DllMain() function under Windows).")
cmd_line_parser.add_option("-e", "--error", action='store_false', dest='generate_error_arg', help="Don't add error output args.")
cmd_line_parser.add_option("-n", "--nothrow", action='store_false', dest='generate_exception_handling_code', help="Don't generate exception handling code.")
cmd_line_parser.add_option("-v", "--verbose", action='store_false', dest='is_verbose', help="Don'e generate verbose output.")
cmd_line_parser.add_option("-9", "--c99", action='store_true', dest='is_c99', help="Compiler with C99 support.")
cmd_line_parser.add_option("-o", "--operator", action='store_false', dest='generate_operators', help="Don't generate operators.")
cmd_line_parser.add_option("-s", "--string", action='store_false', dest='is_compact_string', help="Output std::string in a compact format.")
cmd_line_parser.add_option("--camel", action='store_true', dest='is_camel_case', help="The functions would be outputed in (Upper) Camel Case conventions, not Python conventions (e.g.: FuncName and not func_name).")
cmd_line_parser.add_option("--copy", action='store_true', dest='is_assume_copy', help="Assume public copy constructor for class declarations with no concrete classes.")
cmd_line_parser.add_option("--assign", action='store_true', dest='is_assume_assign', help="Assume public default constructor and assignment operator for class delarations with no concrete classes.")
(options, args) = cmd_line_parser.parse_args()
if len(args) != 1:
cmd_line_parser.error("<header_file_path> is required")
safe_config_get = None
safe_config_get_bool = None
if options.config_file_path is not None:
config = ConfigParser.RawConfigParser()
config.read(options.config_file_path)
safe_config_get = safe_config_get_wrapper(config.get)
safe_config_get_bool = safe_config_get_wrapper(config.getboolean)
generate_dl = parse_option(safe_config_get_bool, 'Cpp2C Config', 'generate_dl', options, True)
generate_error_arg = parse_option(safe_config_get_bool, 'Cpp2C Config', 'generate_error_arg', options, True)
ignore_unsupported_features = parse_option(safe_config_get_bool, 'Cpp2C Config', 'ignore_unsupported_features', options, True)
generate_exception_handling_code = parse_option(safe_config_get_bool, 'Cpp2C Config', 'generate_exception_handling_code', options, True)
gccxml_file_path = parse_option(safe_config_get, 'GccXml Config', 'gccxml_file_path', options, "")
compiler_type = parse_option(safe_config_get, 'GccXml Config', 'compiler_type', options, None)
is_verbose = parse_option(safe_config_get_bool, 'Cpp2C Config', 'is_verbose', options, True)
is_c99 = parse_option(safe_config_get_bool, 'Cpp2C Config', 'is_c99', options, False)
generate_operators = parse_option(safe_config_get_bool, 'Cpp2C Config', 'generate_operators', options, True)
is_compact_string = parse_option(safe_config_get_bool, 'Cpp2C Config', 'is_compact_string', options, True)
is_camel_case = parse_option(safe_config_get_bool, 'Cpp2C Config', 'is_camel_case', options, False)
is_assume_copy = parse_option(safe_config_get_bool, 'Cpp2C Config', 'is_assume_copy', options, False)
is_assume_assign = parse_option(safe_config_get_bool, 'Cpp2C Config', 'is_assume_assign', options, False)
generate_c_wrapper(args[0], generate_dl, generate_exception_handling_code, is_compact_string, is_assume_copy, is_assume_assign, generate_error_arg, is_verbose, generate_operators, ignore_unsupported_features, is_c99, is_camel_case, gccxml_file_path, compiler_type)
print "Done."
@YashMakan
Copy link

can you please tell how to pass the c++ file and get c file. Moreover gr8 program...

@YashMakan
Copy link

anyone??

@YashMakan
Copy link

from where i can get the header file?? please help its urgent...

@katyo
Copy link
Author

katyo commented Nov 26, 2020

This is very old tool which is incomplete yet.
And I'm not an original author.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment