3
0
mirror of https://github.com/triqs/dft_tools synced 2024-12-25 05:43:40 +01:00

[clang tool] add parameter class treatment and h5 generator

wrapper generator: add treatment of parameter class

- methods taking a parameter class are called by **kw
- the dict is passed to the C++
- the converters for the parameters are generated.
This commit is contained in:
Olivier Parcollet 2014-09-20 22:13:14 +02:00
parent 0a1285405c
commit ed6379ce63
12 changed files with 410 additions and 61 deletions

View File

@ -373,7 +373,7 @@ g.add_method(name = "set_from_legendre",
g.add_method(name = "fit_tail",
signature = "void(tail_view known_moments, int n_moments, int n_min, int n_max, bool replace_by_fit = true)",
calling_pattern = "fit_tail(self_c, known_moments, n_moments, n_min, n_max, replace_by_fit)",
calling_pattern = "fit_tail(self_c, *known_moments, n_moments, n_min, n_max, replace_by_fit)",
doc = """Set the tail by fitting""")
# Pure python methods

View File

@ -5,10 +5,16 @@ SET(PYTHON_SOURCES
)
install(FILES ${PYTHON_SOURCES} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/triqs/wrap_generator)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/clang_parser.py DESTINATION bin)
# The desc_file generator from libclang ...
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/wrapper_desc_generator.py.in ${CMAKE_CURRENT_BINARY_DIR}/wrapper_desc_generator.py @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/wrapper_desc_generator.py DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE )
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/clang_parser.py DESTINATION bin)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/wrap_desc.mako.py DESTINATION share/triqs/wrap_generator/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/converters.mako.hxx DESTINATION share/triqs/wrap_generator/)
# The param generator
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/param_generator.py.in ${CMAKE_CURRENT_BINARY_DIR}/param_generator.py @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/param_generator.py DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE )
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/param.mako.cxx DESTINATION share/triqs/wrap_generator/)

View File

@ -20,6 +20,11 @@ def process_doc (doc) :
file_locations = set(())
def decay(s) :
for tok in ['const', '&&', '&'] :
s = re.sub(tok,'',s)
return s.strip()
class member_(object):
def __init__(self, cursor,ns=()):
loc = cursor.location.file.name
@ -28,8 +33,15 @@ class member_(object):
self.ns = ns
self.name = cursor.spelling
self.access = cursor.access_specifier
self.type = type_(cursor.type)
self.ctype = cursor.type.spelling
self.annotations = get_annotations(cursor)
# the declaration split in small tokens
tokens = [t.spelling for t in cursor.get_tokens()]
self.initializer = None
if '=' in tokens:
self.initializer = ''.join(tokens[tokens.index('=') + 1:tokens.index(';')])
def namespace(self) :
return "::".join(self.ns)
@ -37,6 +49,10 @@ class type_(object):
def __init__(self, cursor):
self.name, self.canonical_name = cursor.spelling, cursor.get_canonical().spelling
def __repr__(self) :
return "type : %s"%(self.name)
#return "type : %s %s\n"%(self.name, self.canonical_name)
class Function(object):
def __init__(self, cursor, is_constructor = False, ns=() ): #, template_list =()):
loc = cursor.location.file.name
@ -51,7 +67,7 @@ class Function(object):
self.template_list = [] #template_list
self.is_constructor = is_constructor
self.is_static = cursor.is_static_method()
self.parameter_arg = None # If exists, it is the parameter class
for c in cursor.get_children():
if c.kind == clang.cindex.CursorKind.TEMPLATE_TYPE_PARAMETER :
self.template_list.append(c.spelling)
@ -60,12 +76,25 @@ class Function(object):
for ch in c.get_children() :
# TODO : string literal do not work.. needs to use location ? useful ?
if ch.kind in [clang.cindex.CursorKind.INTEGER_LITERAL, clang.cindex.CursorKind.FLOATING_LITERAL,
clang.cindex.CursorKind.CHARACTER_LITERAL, clang.cindex.CursorKind.STRING_LITERAL] :
default_value = ch.get_tokens().next().spelling
clang.cindex.CursorKind.CHARACTER_LITERAL, clang.cindex.CursorKind.STRING_LITERAL,
clang.cindex.CursorKind.UNARY_OPERATOR, clang.cindex.CursorKind.UNEXPOSED_EXPR,
clang.cindex.CursorKind.CXX_BOOL_LITERAL_EXPR ] :
#print [x.spelling for x in ch.get_tokens()]
#default_value = ch.get_tokens().next().spelling
default_value = ''.join([x.spelling for x in ch.get_tokens()][:-1])
t = type_(c.type)
# We look if this argument is a parameter class...
if 'use_parameter_class' in self.annotations :
tt = c.type.get_declaration() # guess it is not a ref
if not tt.location.file : tt = c.type.get_pointee().get_declaration() # it is a T &
#if tt.raw_comment and 'triqs:is_parameter' in tt.raw_comment:
self.parameter_arg = Class(tt, ns)
self.params.append ( (t, c.spelling, default_value ))
#else :
# print " node in fun ", c.kind
if self.parameter_arg : assert len(self.params) == 1, "When using a parameter class, it must have exactly one argument"
self.rtype = type_(cursor.result_type) if not is_constructor else None
def namespace(self) :
@ -128,7 +157,9 @@ class Class(object):
def namespace(self) :
return "::".join(self.ns)
def canonical_name(self) : return self.namespace() + '::' + self.name
def __str__(self) :
s,s2 = "class {name}:\n {doc}\n\n".format(**self.__dict__),[]
for m in self.members :
@ -140,12 +171,15 @@ class Class(object):
s2 = '\n'.join( [ " " + l.strip() + '\n' for l in s2 if l.strip()])
return s + s2
def __repr__(self) :
return "Class %s"%self.name
def build_functions_and_classes(cursor, namespaces=[]):
classes,functions = [],[]
for c in cursor.get_children():
if (c.kind == clang.cindex.CursorKind.FUNCTION_DECL
and c.location.file.name == sys.argv[1]):
functions.append( Function(c,namespaces))
functions.append( Function(c,is_constructor = False, ns =namespaces))
elif (c.kind in [clang.cindex.CursorKind.CLASS_DECL, clang.cindex.CursorKind.STRUCT_DECL]
and c.location.file.name == sys.argv[1]):
classes.append( Class(c,namespaces))
@ -163,6 +197,7 @@ def parse(filename, debug, compiler_options, where_is_libclang):
clang.cindex.Config.set_library_file(where_is_libclang)
index = clang.cindex.Index.create()
print "Parsing the C++ file (may take a few seconds) ..."
#print filename, ['-x', 'c++'] + compiler_options
translation_unit = index.parse(filename, ['-x', 'c++'] + compiler_options)
print "... done. \nExtracting ..."
@ -180,9 +215,9 @@ def parse(filename, debug, compiler_options, where_is_libclang):
print "... done"
global file_locations
if len(file_locations) != 1 :
print file_locations
raise RuntimeError, "Multiple file location not implemented"
#if len(file_locations) != 1 :
# print file_locations
# raise RuntimeError, "Multiple file location not implemented"
file_locations = list(file_locations)
if debug :

View File

@ -0,0 +1,109 @@
// DO NOT EDIT
// Generated automatically using libclang using the command :
// ${shell_command}
%for c in classes :
<%
def name_lmax(member_list) :
return max(len(m.name) for m in member_list)
def name_format(name) :
f = '{:<%s}'%(name_lmax(c.members)+2)
return f.format(name)
def name_format_q(name) : return name_format('"%s"'%name)
def type_format(name) :
f = '{:<%s}'%(max(len(m.ctype) for m in c.members))
return f.format(name)
%>
// --- C++ Python converter for ${c.name}
namespace triqs { namespace py_tools {
template <> struct py_converter<${c.name}> {
static PyObject *c2py(${c.name} const & x) {
PyObject * d = PyDict_New();
%for m in c.members :
PyDict_SetItemString( d, ${name_format('"%s"'%m.name)}, convert_to_python(x.${m.name}));
%endfor
return d;
}
template <typename T, typename U> static void _get_optional(PyObject *dic, const char *name, T &r, U const &init_default) {
if (PyDict_Contains(dic, pyref::string(name)))
r = convert_from_python<T>(PyDict_GetItemString(dic, name));
else
r = init_default;
}
static ${c.name} py2c(PyObject *dic) {
${c.name} res;
%for m in c.members :
%if m.initializer == None :
res.${m.name} = convert_from_python<${m.ctype}>(PyDict_GetItemString(dic, "${m.name}"));
%else:
_get_optional(dic, ${name_format_q(m.name)}, res.${name_format(m.name)}, ${m.initializer});
%endif
%endfor
return res;
}
template <typename T>
static void _check(PyObject *dic, std::stringstream &fs, int &err, const char *name, const char *tname) {
if (!convertible_from_python<T>(PyDict_GetItemString(dic, name), false))
fs << "\n" << ++err << " The parameter " << name << " does not have the right type : expecting " << tname
<< " in C++, but got '" << PyDict_GetItemString(dic, name)->ob_type->tp_name << "' in Python.";
}
template <typename T>
static void _check_mandatory(PyObject *dic, std::stringstream &fs, int &err, const char *name, const char *tname) {
if (!PyDict_Contains(dic, pyref::string(name)))
fs << "\n" << ++err << " Mandatory parameter " << name << " is missing.";
else _check<T>(dic,fs,err,name,tname);
}
template <typename T>
static void _check_optional(PyObject *dic, std::stringstream &fs, int &err, const char *name, const char *tname) {
if (PyDict_Contains(dic, pyref::string(name))) _check<T>(dic, fs, err, name, tname);
}
static bool is_convertible(PyObject *dic, bool raise_exception) {
if (!PyDict_Check(dic)) {
if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Not a python dict");}
return false;
}
std::stringstream fs, fs2; int err=0;
#ifndef TRIQS_ALLOW_UNUSED_PARAMETERS
std::vector<std::string> ks, all_keys = {${','.join('"%s"'%m.name for m in c.members)}};
pyref keys = PyDict_Keys(dic);
if (!convertible_from_python<std::vector<std::string>>(keys, true)) {
fs << "\nThe dict keys are not strings";
goto _error;
}
ks = convert_from_python<std::vector<std::string>>(keys);
for (auto & k : ks)
if (std::find(all_keys.begin(), all_keys.end(), k) == all_keys.end())
fs << "\n"<< ++err << " The parameter '" << k << "' is not recognized.";
#endif
%for m in c.members :
%if m.initializer == None :
_check_mandatory<${type_format(m.ctype)}>(dic, fs, err, ${name_format_q(m.name)}, "${m.ctype}");
%else:
_check_optional <${type_format(m.ctype)}>(dic, fs, err, ${name_format_q(m.name)}, "${m.ctype}");
%endif
%endfor
if (err) goto _error;
return true;
_error:
fs2 << "\n---- There " << (err > 1 ? "are " : "is ") << err<< " error"<<(err >1 ?"s" : "")<< " in Python -> C++ transcription for the class ${c.name}\n" <<fs.str();
if (raise_exception) PyErr_SetString(PyExc_TypeError, fs2.str().c_str());
return false;
}
};
}}
%endfor

View File

@ -0,0 +1,47 @@
// DO NOT EDIT
// Generated automatically using libclang using the command :
// ${shell_command}
%for c in classes :
<%
def name_lmax(member_list) :
return max(len(m.name) for m in member_list)
def name_format(name) :
f = '{:<%s}'%(name_lmax(c.members)+2)
return f.format(name)
def name_format_q(name) : return name_format('"%s"'%name)
def type_format(name) :
f = '{:<%s}'%(max(len(m.ctype) for m in c.members))
return f.format(name)
%>
${''.join('namespace %s {'%n for n in c.ns)}
/// Write into HDF5
void h5_write(h5::group fg, std::string subgroup_name, ${c.name} const &x) {
auto gr = fg.create_group(subgroup_name);
gr.write_triqs_hdf5_data_scheme("TRIQS_PARAM:${c.name}");
%for m in c.members:
h5_write(gr, ${name_format_q(m.name)}, x.${m.name});
%endfor
}
/// Read from HDF5
void h5_read(h5::group fg, std::string subgroup_name, ${c.name} &x) {
auto gr = fg.open_group(subgroup_name);
// Check the attribute or throw
auto tag_file = gr.read_triqs_hdf5_data_scheme();
auto tag_expected = "TRIQS_PARAM:${c.name}";
if (tag_file != tag_expected)
TRIQS_RUNTIME_ERROR << "h5_read : mismatch of the tag TRIQS_HDF5_data_scheme tag in the h5 group : found " << tag_file
<< " while I expected " << tag_expected;
%for m in c.members:
h5_read(gr, ${name_format_q(m.name)}, x.${m.name});
%endfor
}
## close namespace
${'}'*len(c.ns)}
%endfor

View File

@ -0,0 +1,52 @@
#!@PYTHON_INTERPRETER@
from clang_parser import parse
import sys, os
from mako.template import Template
# the instruction that created this file
shell_command = ' '.join( [ sys.argv[0].rsplit('/')[-1]] + [x if ' ' not in x else '"%s"'%x for x in sys.argv[1:]])
#
print "Welcome to the C++ code generator of TRIQS, based on libclang !"
# --- Parsing the arguments of the script and options
import argparse
parser = argparse.ArgumentParser(description='From a regular class, generate the h5_read/h5_write, mpi, python interface for a parameter class')
parser.add_argument('filename', help = "Name of the file")
parser.add_argument('--libclang_location', help='Location of the libclang', default = '@TRIQS_LIBCLANG_LOCATION@')
parser.add_argument('--compiler_options', nargs ='*', help='Options to pass to clang')
parser.add_argument('--includes', '-I', action='append', help='Includes to pass to clang')
args = parser.parse_args()
args.includes = (args.includes or []) + '@TRIQS_INCLUDE_ALL@'.split(';')
triqs_install_location = '@CMAKE_INSTALL_PREFIX@'
args.includes.insert(0, triqs_install_location + '/include')
#------------
if __name__ == '__main__' :
compiler_options = args.compiler_options or []
compiler_options += ['-I%s'%x for x in args.includes]
add_opts = '@TRIQS_LIBCLANG_CXX_ADDITIONAL_FLAGS@'.strip()
if add_opts:
compiler_options += add_opts.split()
functions, classes = parse(args.filename, debug = False, compiler_options = compiler_options, where_is_libclang = args.libclang_location)
print "Generating ..."
tpl = Template(filename=triqs_install_location + '/share/triqs/wrap_generator/param.mako.cxx')
shell_command = ' '.join( [ sys.argv[0].rsplit('/')[-1]] + sys.argv[1:])
rendered = tpl.render(classes = classes, functions = functions, args = args, shell_command= shell_command )
with open(args.filename.replace('.hpp',".hxx"), "w") as f:
f.write(rendered)
print "... done"

View File

@ -4,8 +4,9 @@
return ''.join([x.capitalize() for x in s.split('_')])
def decay(s) :
for tok in ['const', '&&', '&'] :
for tok in ['const ', 'const&', '&&', '&'] :
s = re.sub(tok,'',s)
s = s.replace('const_view', 'view') # DISCUSS THIS
return s.strip()
# compute used_module_list
@ -15,12 +16,19 @@
'triqs::utility::many_body_operator' : 'operators2',
}
using_needed_for_modules = {
'gf' : 'namespace triqs::gfs',
'parameters' : 'namespace triqs::params',
'operators2' : 'triqs::utility::many_body_operator',
}
used_module_list = []
def analyse(t) :
if t is None :return
#global used_module_list
for ns, mod in recognized_namespace_for_using.items() :
if decay(t.canonical_name).startswith(ns) :
used_module_list.append(mod)
used_module_list.append(mod)
for c in classes :
for m in c.constructors :
@ -31,16 +39,22 @@
for p in c.proplist :
analyse(p.getter.rtype)
for c in classes_of_parameters :
for m in c.members :
analyse(m.type)
for f in functions :
for t,n,d in f.params : analyse(t)
analyse(f.rtype)
used_module_list = set(used_module_list) # makes unique
using_list = [using_needed_for_modules[m] for m in used_module_list]
def cls(t) :
tname = decay(t.name)
if 'gf' in used_module_list: tname = re.sub('triqs::gfs::','',tname)
if 'parameters' in used_module_list: tname = re.sub('triqs::params::','',tname)
tname = tname.replace(' ','')
return tname
def make_signature(m) :
@ -48,47 +62,74 @@
s = "({args})"
if not m.is_constructor :
s = cls(m.rtype) + " {name} " + s
s = s.format(args = ', '.join( ["%s %s"%(cls(t),n) + (" = %s"%d if d else "") for t,n,d in m.params]), **m.__dict__)
args = ', '.join( ["%s %s"%(cls(t),n) + (" = %s"%d if d else "") for t,n,d in m.params]) if m.parameter_arg == None else '**%s'%cls(m.params[0][0])
s = s.format(args = args, **m.__dict__)
return s.strip()
%>
##
##
# Generated automatically using libclang using the command :
# Generated automatically using the command :
# ${shell_command}
from wrap_generator import *
# The module
module = module_(full_name = "${modulename}", doc = " ")
module = module_(full_name = "${modulename}", doc = "${moduledoc}")
# All the triqs C++/Python modules
%for mod in used_module_list :
module.use_module('${mod}')
%endfor
##
## All the using
##%for ns in using_list :
##module.add_using('${ns}')
##%endfor
# Add here all includes beyond what is automatically included by the triqs modules
module.add_include("${args.filename}")
# Add here anything to add in the C++ code at the start, e.g. namespace using
module.add_preamble("""
// using namespace XXX;
%for ns in using_list :
using ${ns};
%endfor
%for c in classes :
%for ns in c.ns :
using namespace ${ns};
%endfor
%endfor
%if classes_of_parameters :
#include "./converters.hxx"
%endif
""")
##
%for c in classes :
<%
def doc_format(member_list) :
h= ['Parameter Name', 'Type', 'Default', 'Documentation']
n_lmax = max(len(h[0]), max(len(m.name) for m in member_list))
type_lmax = max(len(h[1]), max(len(m.ctype) for m in member_list))
opt_lmax = max(len(h[2]), max(len(m.initializer) for m in member_list if m.initializer))
doc_lmax = max(len(h[3]), max(len(m.doc) for m in member_list))
form = " {:<%s} {:<%s} {:<%s} {:<%s}"%(n_lmax, type_lmax, opt_lmax, doc_lmax)
header = form.format(*h)
r = '\n'.join( form.format(m.name, m.ctype, m.initializer if m.initializer else '--', m.doc) for m in member_list)
return header + '\n\n' + r
%>
# The class ${c.name}
g = class_(
c = class_(
py_type = "${deduce_normalized_python_class_name(c.name)}", # name of the python class
c_type = "${c.name}", # name of the C++ class
%if 0 :
#
#Hereafter several options to be selected by hand. Cf doc
#has_iterator = True,
#boost_serializable= True,
#is_printable= True,
#arithmetic = ("algebra","double")
)
%endif
)
%for m in c.members :
g.add_member(c_name = "${m.name}",
c.add_member(c_name = "${m.name}",
c_type = "${m.ctype}",
read_only= False,
doc = """${m.doc} """)
@ -96,23 +137,22 @@ g.add_member(c_name = "${m.name}",
%endfor
##
%for m in [m for m in c.constructors if not m.is_template]:
g.add_constructor("${make_signature(m)}",
c.add_constructor("""${make_signature(m)}""",
doc = """${m.doc} """)
%endfor
##
##
%for m in [m for m in c.methods]:
g.add_method("${make_signature(m)}",
%for m in c.methods:
c.add_method("""${make_signature(m)}""",
%if m.is_static :
is_static = True,
%endif
doc = """${m.doc} """)
doc = """${m.doc if m.parameter_arg==None else doc_format(m.parameter_arg.members) } """)
%endfor
##
%for p in [p for p in c.proplist]:
g.add_property(name = "${p.name}",
c.add_property(name = "${p.name}",
getter = cfunction("${make_signature(p.getter)}"),
%if p.setter :
setter = cfunction("${make_signature(p.setter)}"),
@ -121,7 +161,7 @@ g.add_property(name = "${p.name}",
%endfor
##
module.add_class(g)
module.add_class(c)
%endfor
##

View File

@ -79,6 +79,7 @@ class cfunction :
self.doc = doc
self.is_method = is_method
self.is_static = is_static
self._dict_call = None
if is_static : assert is_method, "is_static only works with method"
self.release_GIL_and_enable_signal = release_GIL_and_enable_signal
assert isinstance(signature, str) or isinstance(signature, dict), "Signature must be a string of a dict: cf doc"
@ -95,7 +96,10 @@ class cfunction :
if not is_constructor and len(spl)> 1 and '>' not in spl[-1] :
self.rtype, self.c_name = spl
if self.c_name == '__operator_call' : self.c_name = "operator()"
if args.strip().startswith("**") : # special case : dict call
assert calling_pattern is None, "When ** is given as argument, no calling pattern can be provided"
self._dict_call = args[2:]
args, self.args = '','' # no argument
def f(): # analyse the argument, be careful that , can also be in type, like A<B,C>, so we count the < >
acc = ''
for s in args.split(',') :
@ -103,7 +107,14 @@ class cfunction :
if acc.count('<') == acc.count('>') :
r, acc = acc,''
yield r
args = [ re.sub('=',' ',x).split() for x in f() if x] # list of (type, name, default) or (type, name)
def g(a) :
if '=' in a :
l,r = a.split('=')
return l.strip().rsplit(' ') + [r]
else :
return a.rsplit(' ',1)
#args = [ re.sub('=',' ',x).split() for x in f() if x] # list of (type, name, default) or (type, name)
args = [ g(x) for x in f() if x] # list of (type, name, default) or (type, name)
else :
self.rtype = signature.pop("rtype", None)
args = signature.pop('args',())
@ -116,7 +127,7 @@ class cfunction :
if len(a) == 2 : (t,n),d = a,None
elif len(a) == 3 : t,n,d = a
else : raise RuntimeError, "Syntax error in overload: args = %s"%args
self.args.append([t,n,d])
self.args.append([t.strip(),n.strip(),d])
# end analyze signature
assert self.c_name or self._calling_pattern or self.is_constructor, "You must specify a calling_pattern or the signature must contain the name of the function"
if self.is_constructor :
@ -126,12 +137,15 @@ class cfunction :
def _get_calling_pattern(self) :
"""Generation only: gets the calling_pattern or synthesize the default"""
if self._calling_pattern : return self._calling_pattern
s= "%s result = "%self.rtype if self.rtype != "void" else ""
s1 = '' if self._dict_call is None else "if (!convertible_from_python<%s>(keywds,true)) goto error_return;\n"%self._dict_call
s = "%s result = "%self.rtype if self.rtype != "void" else ""
self_c = ""
if self.is_method:
self_c = "self_c." if not self.is_static else "self_class::"
# the wrapped types are called by pointer
return "%s %s%s(%s)"%(s,self_c, self.c_name , ",".join([ ('*' if t in module_._wrapped_types else '') + n for t,n,d in self.args]))
args = ",".join([ ('*' if t in module_._wrapped_types else '') + n for t,n,d in self.args])
args = args if self._dict_call is None else "convert_from_python<%s>(keywds)"%self._dict_call # call with the keywds argument
return "%s %s %s%s(%s)"%(s1, s,self_c, self.c_name , args)
def _get_signature (self):
"""Signature for the python doc"""
@ -160,6 +174,9 @@ class cfunction :
def _generate_doc(self) :
doc = "\n".join([ " " + x.strip() for x in self.doc.split('\n')])
doc = doc.replace('"',"'") # the " are replaced by \"r.
#doc = doc.replace('"',r'\"') # the " are replaced by \"r. Does not work, makes \\"
if self._dict_call is not None : return doc
return "Signature : %s\n%s"%( self._get_signature(),doc)
class pure_pyfunction_from_module :
@ -234,7 +251,10 @@ class pyfunction :
self.overloads.append(cfunction(**kw))
def _generate_doc(self) :
s = "\n".join([self.doc, "\n"] + [f._generate_doc() for f in self.overloads])
if len(self.overloads) == 1 : #only one overload
s = "\n".join([f._generate_doc() for f in self.overloads])
else :
s = "\n".join([self.doc, "\n"] + [f._generate_doc() for f in self.overloads])
return repr(s)[1:-1] # remove the ' ' made by repr
def _is_type_a_view(c_type) :
@ -417,7 +437,7 @@ class class_ :
all_args = ",".join([ ('*' if t in module_._wrapped_types else '') + n for t,n,d in f.args])
f._calling_pattern = ''
if calling_pattern is not None :
f._calling_pattern, all_args = calling_pattern + '\n', "std::move(result)"
f._calling_pattern, all_args = calling_pattern + ';\n', "std::move(result)"
if self.c_type_is_view and build_from_regular_type_if_view :
f._calling_pattern += "((%s *)self)->_c = new %s(%s (%s));"%(self.py_type, self.c_type,_regular_type_if_view_else_type(self.c_type),all_args)
else :

View File

@ -555,6 +555,7 @@ template<typename T>
// If no overload, we avoid the err_list and let the error go through (to save some code).
%for n_overload, overload in enumerate(py_meth.overloads) :
{// overload ${overload._get_c_signature()}
%if not overload._dict_call :
// define the variable to be filled by the parsing method
// wrapped types are converted to a pointer, other converted types to a value or a view
%for t,n,d in overload.args :
@ -568,7 +569,9 @@ template<typename T>
%endfor
static char *kwlist[] = {${",".join([ '"%s"'%n for t,n,d in overload.args] + ["NULL"])}};
static const char * format = "${overload._parsing_format()}";
if (PyArg_ParseTupleAndKeywords(args, keywds, format, kwlist ${"".join([ module._get_proper_converter(t) + ' ,&%s'%n for t,n,d in overload.args])})) {
if (PyArg_ParseTupleAndKeywords(args, keywds, format, kwlist ${"".join([ module._get_proper_converter(t) + ' ,&%s'%n for t,n,d in overload.args])}))
%endif
{
%if overload.is_method and not overload.is_constructor and not overload.no_self_c and not overload.is_static :
auto & self_c = convert_from_python<${self_c_type}>(self);
%endif

View File

@ -1,24 +1,37 @@
#!@PYTHON_INTERPRETER@
from clang_parser import parse
from clang_parser import parse, decay
import sys, os
from mako.template import Template
# the instruction that created this file
shell_command = ' '.join( [ sys.argv[0].rsplit('/')[-1]] + [x if ' ' not in x else '"%s"'%x for x in sys.argv[1:]])
#
print "Welcome to the wrapper desc file generator of TRIQS, based on libclang !"
# macro vim to replace the old param class ...
# :'<,'>s/\s*\.add_field("\(.\{-}\)",\s*\(.\{-}\)(\(.\{-}\)),\s*"\(.\{-}\)")/\/\/\/\4\r\2 \1 = \3;\r/g
# --- Parsing the arguments of the script and options
import argparse
parser = argparse.ArgumentParser(description='C++/Python wrapper desc file generator from C++ header code')
parser = argparse.ArgumentParser(description="""
Generate the C++/Python wrapper desc file from C++ header code
""")
parser.add_argument('filename', help = "Name of the file")
parser.add_argument('--modulename', help='Name of the Python module', default = '')
parser.add_argument('--libclang_location', help='Location of the libclang', default = '@TRIQS_LIBCLANG_LOCATION@')
parser.add_argument('--compiler_options', nargs ='*', help='Options to pass to clang')
parser.add_argument('--includes', '-I', action='append', help='Includes to pass to clang')
parser.add_argument('--outputname', '-o', help="Name of the xxx_desc.py file [default is same as the filename]", default = '')
parser.add_argument('--modulename', '-m', help="Name of the Python module [default ='', it will take the name of file", default = '')
parser.add_argument('--moduledoc', help="Documentation of the module", default = '')
parser.add_argument('--properties', '-p', action='store_true',
help="""Transforms i) every method with no arguments into read-only property
ii) every method get_X into read-only property
iii) every couple of methods get_X, set_X into rw property
""")
parser.add_argument('--libclang_location', help='Location of the libclang', default = '@TRIQS_LIBCLANG_LOCATION@')
parser.add_argument('--compiler_options', nargs ='*', help='Options to pass to clang')
parser.add_argument('--includes', '-I', action='append', help='Includes to pass to clang')
args = parser.parse_args()
args.includes = (args.includes or []) + '@TRIQS_INCLUDE_ALL@'.split(';')
@ -29,6 +42,7 @@ args.includes.insert(0, triqs_install_location + '/include')
#------------
modulename = args.modulename or os.path.split(args.filename)[1].split('.',1)[0]
output_name = args.outputname or os.path.split(args.filename)[1].split('.',1)[0]
class property_ :
def __init__ (self, **kw) :
@ -55,31 +69,50 @@ if __name__ == '__main__' :
m_by_names =dict( (m.name,m) for m in cls.methods)
# Find all the couples get_X, set_X
for m in cls.methods :
if m.is_template or m.name.startswith('operator') or m.name in ['begin','end'] :
if m.is_template or m.name.startswith('operator') or m.name in ['begin','end'] : # or m.rtype.name=="void" :
exclude.append(m)
elif m.name.startswith('get_') :
X = m.name[4:]
set_m = m_by_names.get('set_' + X, None)
if set_m and set_m.rtype == "void" and len(set_m.params_decay) ==1 :
if set_m.params_decay[0][0] == m.rtype :
cls.proplist.append(property_(name= X, doc = m.doc, getter = m, setter = set_m))
exclude += [m,set_m]
elif len(m.params) == 0 and not m.is_static : # it is a property
X = m.name[4:] if m.name.startswith('get_') else m.name # remove the get_ if present
set_m = m_by_names.get('set_' + X, None)
if set_m and set_m.rtype.name == "void" and len(set_m.params) ==1 :
if decay(set_m.params[0][0].name) == m.rtype.name :
exclude.append(set_m)
else :
print "Warning :"
print " in get_%s/set_%s" %(X,X)
print " The type taken from set_%s is not the return type of get_%s"%(X,X)
print " I am not transforming to property"
elif len(m.params) == 0 and not m.is_static : # it is a property not starting with get_, pure getter
cls.proplist.append(property_(name= m.name, doc = m.doc, getter = m, setter = None))
print " Expected ",m.rtype.name
print " Got ", decay(set_m.params[0][0].name)
print " I am not adding the setter to the property"
set_m = None
cls.proplist.append(property_(name= X, doc = m.doc, getter = m, setter = set_m))
print "Property : ", m.name, set_m.name if set_m else ''
exclude.append(m)
cls.methods = [m for m in cls.methods if m not in exclude]
# all classes used as parameters needs to be converted from / to Python
classes_of_parameters = [m.parameter_arg for m in sum((c.methods for c in classes),[]) if m.parameter_arg != None]
classes_of_parameters = dict(((c.name,c) for c in classes_of_parameters)).values() # make unique
if classes_of_parameters :
print "Generating the converters for parameters classes : " + ', '.join([c.name for c in classes_of_parameters])
# Generate the converter code for all the classes.
tpl = Template(filename=triqs_install_location + '/share/triqs/wrap_generator/converters.mako.hxx')
rendered = tpl.render(classes = classes_of_parameters, args = args, shell_command= shell_command )
#with open(args.filename.replace('.hpp',".hxx"), "w") as f:
with open("converters.hxx", "w") as f:
f.write(rendered)
print "... done"
# Making the desc file
tpl = Template(filename=triqs_install_location + '/share/triqs/wrap_generator/wrap_desc.mako.py')
rendered = tpl.render(classes = classes, functions = functions, modulename=modulename, args = args, shell_command= ' '.join(sys.argv) )
rendered = tpl.render(classes = classes, functions = functions, modulename=modulename, moduledoc=args.moduledoc, args = args,
shell_command= shell_command, classes_of_parameters = classes_of_parameters )
with open("{modulename}_desc.py".format(modulename=modulename), "w") as f:
with open("{output_name}_desc.py".format(output_name=output_name), "w") as f:
f.write(rendered)
print "... done"
print "... done"

View File

@ -53,4 +53,4 @@ namespace triqs { namespace gfs {
int n_max, bool replace_by_fit = false) ;
void fit_tail(gf_view<imfreq, scalar_valued> gf, tail_view known_moments, int n_moments, int n_min, int n_max, bool replace_by_fit = false) ;
}}} // namespace
}} // namespace

View File

@ -516,6 +516,10 @@ template <typename... T> struct py_converter<triqs::gfs::gf_view<triqs::gfs::blo
}
};
// Treat the block_gf as view
template <typename... T> struct py_converter<triqs::gfs::gf<triqs::gfs::block_index, triqs::gfs::gf<T...>>> :
py_converter<triqs::gfs::gf_view<triqs::gfs::block_index, triqs::gfs::gf<T...>>> {};
// Converter for scalar_valued gf : reinterpreted as 1x1 matrix
template <typename Variable, typename Opt> struct py_converter<triqs::gfs::gf_view<Variable, triqs::gfs::scalar_valued, Opt>>{