mirror of
https://github.com/triqs/dft_tools
synced 2024-10-31 19:23:45 +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:
parent
0a1285405c
commit
ed6379ce63
@ -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
|
||||
|
@ -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/)
|
||||
|
||||
|
@ -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,7 +33,14 @@ 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) :
|
||||
@ -129,6 +158,8 @@ 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 :
|
||||
|
109
pytriqs/wrap_generator/converters.mako.hxx
Normal file
109
pytriqs/wrap_generator/converters.mako.hxx
Normal 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
|
47
pytriqs/wrap_generator/param.mako.cxx
Normal file
47
pytriqs/wrap_generator/param.mako.cxx
Normal 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
|
52
pytriqs/wrap_generator/param_generator.py.in
Normal file
52
pytriqs/wrap_generator/param_generator.py.in
Normal 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"
|
||||
|
@ -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,8 +16,15 @@
|
||||
'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) :
|
||||
@ -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
|
||||
##
|
||||
|
@ -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
|
||||
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,6 +251,9 @@ class pyfunction :
|
||||
self.overloads.append(cfunction(**kw))
|
||||
|
||||
def _generate_doc(self) :
|
||||
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
|
||||
|
||||
@ -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 :
|
||||
|
@ -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
|
||||
|
@ -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:]
|
||||
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 == "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]
|
||||
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]
|
||||
|
||||
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) )
|
||||
# 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("{modulename}_desc.py".format(modulename=modulename), "w") as f:
|
||||
#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, moduledoc=args.moduledoc, args = args,
|
||||
shell_command= shell_command, classes_of_parameters = classes_of_parameters )
|
||||
|
||||
with open("{output_name}_desc.py".format(output_name=output_name), "w") as f:
|
||||
f.write(rendered)
|
||||
|
||||
print "... done"
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>>{
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user