mirror of
https://github.com/triqs/dft_tools
synced 2024-10-31 19:23:45 +01:00
Add new wrapper generator
- with pytriqs.wrap_test as an example.
This commit is contained in:
parent
2be9587078
commit
5105e04ac7
@ -376,7 +376,7 @@ endif( ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
# pytriqs modules
|
||||
#------------------------
|
||||
if (PythonSupport)
|
||||
message( STATUS "-------- Preparing Cython modules -------------")
|
||||
message( STATUS "-------- Preparing python modules -------------")
|
||||
add_subdirectory(${TRIQS_SOURCE_DIR}/pytriqs )
|
||||
endif (PythonSupport)
|
||||
|
||||
|
@ -54,6 +54,7 @@ IF (PYTHON_VERSION_NOT_OK)
|
||||
MESSAGE(FATAL_ERROR "Python intepreter version is ${PYTHON_VERSION} . It should be >= ${PYTHON_MINIMAL_VERSION}")
|
||||
ENDIF (PYTHON_VERSION_NOT_OK)
|
||||
|
||||
EXEC_PYTHON_SCRIPT ("import mako.template" nulle) # check that Mako is there...
|
||||
EXEC_PYTHON_SCRIPT ("import distutils " nulle) # check that distutils is there...
|
||||
EXEC_PYTHON_SCRIPT ("import numpy" nulle) # check that numpy is there...
|
||||
EXEC_PYTHON_SCRIPT ("import h5py" nulle) # check that h5py is there...
|
||||
|
32
cmake/FindPythonWrapperMacro.cmake
Normal file
32
cmake/FindPythonWrapperMacro.cmake
Normal file
@ -0,0 +1,32 @@
|
||||
# Compile and link with python
|
||||
include_directories(${PYTHON_INCLUDE_DIRS} ${PYTHON_NUMPY_INCLUDE_DIR})
|
||||
|
||||
# This function add the target to build a python module
|
||||
#
|
||||
# ModuleName = the python name of the module
|
||||
# ModuleDest = path in the pytriqs tree [ FOR INSTALLATION ONLY] IMPROVE MAKE THIS OPTIONAL (for test)
|
||||
|
||||
macro (triqs_python_extension ModuleName)
|
||||
message(STATUS "TRIQS: Preparing extension module ${ModuleName} with the interpreter ${TRIQS_PYTHON_INTERPRETER} ")
|
||||
|
||||
SET(wrap_name ${CMAKE_CURRENT_BINARY_DIR}/${ModuleName}_wrap.cpp)
|
||||
|
||||
# Adjust pythonpath so that pytriqs is visible and the wrap_generator too...
|
||||
# pytriqs needed since we import modules with pure python method to extract the doc..
|
||||
add_custom_command(OUTPUT ${wrap_name} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${ModuleName}_desc.py
|
||||
COMMAND PYTHONPATH=${CMAKE_BINARY_DIR}/pytriqs/wrap_generator:${CMAKE_BINARY_DIR}/ ${PYTHON_INTERPRETER} ${CMAKE_CURRENT_BINARY_DIR}/${ModuleName}_desc.py ${CMAKE_SOURCE_DIR}/pytriqs/wrap_generator/wrapper.mako.cpp ${CMAKE_CURRENT_BINARY_DIR}/${ModuleName}_wrap.cpp )
|
||||
|
||||
add_custom_target(python_wrap_${ModuleName} ALL DEPENDS ${wrap_name})
|
||||
add_dependencies(python_wrap_${ModuleName} py_copy)
|
||||
|
||||
add_library(${ModuleName} MODULE ${wrap_name})
|
||||
set_target_properties(${ModuleName} PROPERTIES PREFIX "") #eliminate the lib in front of the module name
|
||||
target_link_libraries(${ModuleName} ${TRIQS_LINK_LIBS} triqs)
|
||||
|
||||
if (${ARGN} MATCHES "")
|
||||
install (TARGETS ${ModuleName} DESTINATION ${TRIQS_PYTHON_LIB_DEST}/${ARGN} )
|
||||
endif (${ARGN} MATCHES "")
|
||||
#set_property (GLOBAL APPEND PROPERTY DEPENDANCE_TO_ADD triqs_${NickName} )
|
||||
|
||||
endmacro (triqs_python_extension)
|
||||
|
@ -12,8 +12,11 @@ add_custom_command (OUTPUT ${py_copy_tar} DEPENDS ${all_py_files}
|
||||
COMMAND cd ${CMAKE_SOURCE_DIR}/pytriqs && tar cf ${py_copy_tar} ${all_py_files} && cd ${CMAKE_BINARY_DIR}/pytriqs && tar xf ${py_copy_tar} )
|
||||
add_custom_target(py_copy ALL DEPENDS ${py_copy_tar})
|
||||
|
||||
# To be removed
|
||||
find_package(CythonModuleMacro)
|
||||
|
||||
find_package(PythonWrapperMacro)
|
||||
|
||||
SET(PYTHON_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/__init__.py
|
||||
${CMAKE_CURRENT_BINARY_DIR}/version.py
|
||||
|
@ -20,7 +20,7 @@
|
||||
#
|
||||
################################################################################
|
||||
|
||||
__all__ = ['archive', 'dmft', 'dos', 'fit', 'gf', 'lattice', 'plot', 'sumk', 'utility', 'parameters']
|
||||
__all__ = ['archive', 'dmft', 'dos', 'fit', 'gf', 'lattice', 'plot', 'sumk', 'utility', 'parameters', 'wrap_test']
|
||||
|
||||
import sys
|
||||
if 'additional_builtin_modules' in dir(sys) :
|
||||
|
512
pytriqs/wrap_generator/wrap_generator.py
Normal file
512
pytriqs/wrap_generator/wrap_generator.py
Normal file
@ -0,0 +1,512 @@
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
from mako.template import Template
|
||||
import importlib
|
||||
|
||||
# the correspondance c type -> py_type
|
||||
c_to_py_type = {'void' : 'None', 'int' : 'int', 'long' : 'int', 'double' : "float", "std::string" : "str"}
|
||||
|
||||
# Translation for formatting
|
||||
basic_types_formatting = {'double' : 'd', 'int' : 'i'}
|
||||
|
||||
def translate_c_type_to_py_type(t) :
|
||||
# mainly for doc signatures...
|
||||
if t in c_to_py_type : return c_to_py_type[t]
|
||||
m = re.match('std::vector<(.*)>',t)
|
||||
if m: return "list[%s]"%translate_c_type_to_py_type(m.group(1))
|
||||
# numpy, etc...
|
||||
return t
|
||||
|
||||
class cfunction :
|
||||
"""
|
||||
Representation of one overload of a C++ function.
|
||||
Data :
|
||||
- c_name : the name of the function in C++
|
||||
- doc : the doc string.
|
||||
- is_constructor : boolean
|
||||
- rtype : the C++ type returned by the function. None for constructor
|
||||
- args : The list of arguments, as [ (c_type, variable_name, default_value)]
|
||||
default_value is None when there is no default.
|
||||
- signature : (incompatible with rtype and args).
|
||||
rtype( arg1 name1, arg2 name2, ....)
|
||||
- calling_pattern : Pattern to rewrite the call of the c++ function,
|
||||
it is a string, using self_c, argument name and defining result at
|
||||
the end if rtype != void
|
||||
e.g., teh default pattern is :
|
||||
auto result = self_c.method_name(a,b,c).
|
||||
INCOMPATIBLE with c_name.
|
||||
If c_name is given, the default calling_pattern is made.
|
||||
"""
|
||||
def __init__(self, doc = '', is_method = False, no_self_c = False, **kw) :
|
||||
""" Use keywords to build, from the data. Cf doc of class"""
|
||||
self.c_name = kw.pop("c_name", None)
|
||||
self._calling_pattern = kw.pop("calling_pattern", None)
|
||||
self.is_constructor = kw.pop("is_constructor", False)
|
||||
self.no_self_c = no_self_c # do not generate self_c reference, in some rare calling_pattern. Avoid a warning.
|
||||
assert self.c_name or self._calling_pattern or self.is_constructor, "You must specify c_name or calling_pattern"
|
||||
assert not(self.c_name and self._calling_pattern), "You can not specify c_name and calling_pattern"
|
||||
self.doc = doc
|
||||
self.is_method = is_method
|
||||
self.args = []
|
||||
if 'signature' in kw :
|
||||
assert 'rtype' not in kw and 'args' not in kw, "signature and rtype/args are not compatible"
|
||||
signature = kw.pop("signature")
|
||||
m = re.match(r"\s*(.*?)\s*\((.*)\)",signature)
|
||||
self.rtype, args = m.group(1).strip() or None, m.group(2).strip()
|
||||
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(',') :
|
||||
acc += (',' if acc else '') + s
|
||||
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)
|
||||
else:
|
||||
self.rtype = kw.pop("rtype", None)
|
||||
args = kw.pop('args',())
|
||||
for a in args: # put back the default if there is none
|
||||
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])
|
||||
#assert len(kw)==0, "unknown parameters %s"%kw.keys()
|
||||
if self.is_constructor :
|
||||
assert self.rtype == None, "Constructor must not have a return type"
|
||||
self.is_method = False
|
||||
|
||||
def calling_pattern(self) :
|
||||
if self._calling_pattern : return self._calling_pattern
|
||||
s= "%s result = "%self.rtype if self.rtype != "void" else ""
|
||||
self_c = "self_c." if self.is_method else ""
|
||||
# the wrapped types are called by pointer !
|
||||
# do we want to keep it this way ?
|
||||
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]))
|
||||
|
||||
def signature (self):
|
||||
"""Signature for the python doc"""
|
||||
name = self.c_name
|
||||
rtype = translate_c_type_to_py_type(self.rtype) if self.rtype else ''
|
||||
args_rep = ", ".join(["%s %s%s"%(translate_c_type_to_py_type(t),n,' = ' + str(d) if d else '') for t,n,d in self.args])
|
||||
return "({args_rep}) -> {rtype}".format(**locals())
|
||||
|
||||
def c_signature (self):
|
||||
"""Signature for the C++ calling errors"""
|
||||
name = self.c_name if self.c_name else "(no C++ name)"
|
||||
rtype = self.rtype if self.rtype else ''
|
||||
args_rep = ", ".join(["%s %s"%(t,n) for t,n,d in self.args])
|
||||
return "{name}({args_rep}) -> {rtype}".format(**locals())
|
||||
|
||||
def __repr__(self):
|
||||
return "C++ function of signature : %s"%(self.signature())
|
||||
|
||||
def format(self) :
|
||||
def f(t) :
|
||||
return basic_types_formatting[t] if t in basic_types_formatting else 'O&'
|
||||
l1 = [ f(t) for t,n,d in self.args if d==None]
|
||||
l2 = [ f(t) for t,n,d in self.args if d!=None]
|
||||
if l2 : l2.insert(0,'|')
|
||||
return ''.join(l1 + l2)
|
||||
|
||||
def generate_doc(self) :
|
||||
doc = "\n".join([ " " + x.strip() for x in self.doc.split('\n')])
|
||||
return "Signature : %s\n%s"%( self.signature(),doc)
|
||||
|
||||
class pyfunction :
|
||||
"""
|
||||
Representation of one python function of the extension
|
||||
Data :
|
||||
- py_name : name given in Python
|
||||
- doc : the doc string.
|
||||
- overloads : a list of cfunction objects representing the various C++ overloads of the function
|
||||
- python_precall : a python function_ to be called before the call of the C++ function
|
||||
The function must take F(*args, **kw) and return (args, kw)
|
||||
- python_postcall : a python function_ to be called after the call of the C++ function
|
||||
The function must take a python object, and return one...
|
||||
- module : module path to the function [pure python only]
|
||||
"""
|
||||
def __init__(self, py_name, is_method = False, doc = '', python_precall = None, python_postcall = None, arity = None, **unused) :
|
||||
""" Use keywords to build, from the data. Cf doc of class"""
|
||||
self.py_name =py_name # name given in python
|
||||
self.doc = doc
|
||||
self.arity = arity
|
||||
self.is_method = is_method # can be a method, a function...
|
||||
self.python_precall, self.python_postcall = python_precall, python_postcall
|
||||
self.overloads = [] # List of all C++ overloads
|
||||
self.do_implement = True # in some cases, we do not want to implement it automatically, (special methods).
|
||||
self.is_constructor = False
|
||||
|
||||
def add_overload(self, **kw) :
|
||||
self.overloads.append(cfunction(**kw))
|
||||
|
||||
#def __repr__(self):
|
||||
# return ""
|
||||
|
||||
def has_pre_post_call(self) : return self.python_precall or self.python_postcall
|
||||
|
||||
def generate_doc(self) :
|
||||
s = "\n".join([self.doc, "\n"] + [f.generate_doc() for f in self.overloads])
|
||||
return repr(s)[1:-1] # remove the ' ' made by repr
|
||||
|
||||
class pure_pyfunction_from_module :
|
||||
"""
|
||||
Representation of one python function defined in Python code in an external module.
|
||||
Will be use to make a pure python method of an object, or or a module.
|
||||
Data :
|
||||
- py_name : name given in Python
|
||||
- doc : the doc string.
|
||||
- module : module path to the function [pure python only]
|
||||
"""
|
||||
def __init__(self, py_name, module, doc = '') :
|
||||
""" """
|
||||
self.py_name, self.module, self.doc = py_name, module, doc
|
||||
try :
|
||||
m = __import__(module.rsplit('.')[-1])
|
||||
f = getattr(m,py_name)
|
||||
self.doc = f.__doc__ # get the doc and check the function can be loaded.
|
||||
except :
|
||||
print " I can not import the function %s from the module %s"%(py_name,module)
|
||||
raise
|
||||
|
||||
#def __repr__(self):
|
||||
#return ""
|
||||
|
||||
def generate_doc(self) :
|
||||
return self.doc
|
||||
|
||||
class python_function:
|
||||
"""
|
||||
A python function, given as a function.
|
||||
Its code gets analysed and will be put into the C++ wrapper, to avoid import.
|
||||
"""
|
||||
def __init__(self, name, f) :
|
||||
""" """
|
||||
self.name, self.f = name,f
|
||||
import inspect as ins
|
||||
self.code = "\n".join(['"%s\\n"'%line.rstrip().replace('"', '\\"') for line in ins.getsourcelines(self.f)[0]])
|
||||
self.doc = f.__doc__ # UNUSED AT THE MOMENT
|
||||
|
||||
class property_ :
|
||||
"""
|
||||
Representation of a property of a class
|
||||
Data :
|
||||
- name : Name, in python
|
||||
- getter : the cfunction representing the get part
|
||||
- setter : the cfunction representing the set part or None if the property if read only
|
||||
- doc : the doc string.
|
||||
"""
|
||||
def __init__(self, name, getter, setter = None, doc = '') :
|
||||
self.name, self.getter, self.setter, self.doc = name, getter, setter, doc
|
||||
|
||||
class member_ :
|
||||
"""
|
||||
Representation of a member of a class
|
||||
Data :
|
||||
- c_name : name of the variable in C++
|
||||
- py_name : name of the variable in python
|
||||
- c_type : type of the C++ variable
|
||||
- read_only : bool
|
||||
- doc : the doc string.
|
||||
"""
|
||||
def __init__(self, c_name, c_type, py_name = None, read_only = False, doc = '') :
|
||||
self.c_name, self.c_type, self.py_name, self.doc, self.read_only = c_name, c_type, py_name or c_name, doc, read_only
|
||||
|
||||
class iterator_ :
|
||||
"""
|
||||
Representation of an iterator
|
||||
Data :
|
||||
- c_type : type of the C++ variable
|
||||
- c_cast_type :
|
||||
- begin, end :
|
||||
"""
|
||||
def __init__(self,c_type = "const_iterator", c_cast_type = None, begin = "std::begin", end = "std::end") :
|
||||
self.c_type, self.c_cast_type, self.begin, self.end = c_type, c_cast_type, begin, end
|
||||
|
||||
def is_type_a_view(c_type) :
|
||||
return c_type.split('<', 1)[0].endswith("_view") # A bit basic ?
|
||||
|
||||
def regular_type_if_view_else_type(c_type) :
|
||||
return "typename %s::regular_type"%c_type if is_type_a_view(c_type) else c_type
|
||||
|
||||
class class_ :
|
||||
"""
|
||||
Representation of a wrapped type
|
||||
Data :
|
||||
- c_type : C++ type to be wrapped.
|
||||
- py_type : Name given in Python
|
||||
- doc : the doc string.
|
||||
- c_type_is_view : boolean
|
||||
- methods : a dict : string -> pyfunction for each method name
|
||||
- constructor : a pyfunction for the constructors.
|
||||
- properties : a dict : string -> property_
|
||||
- members : a dict : string -> member_
|
||||
"""
|
||||
hidden_python_function = {} # global dict of the python function to add to the module, hidden for the user, for precompute and so on
|
||||
def __init__(self, c_type, py_type, hdf5 = False, arithmetic = None, serializable = None, is_printable = False, doc = '' ) :
|
||||
self.c_type = c_type
|
||||
self.c_type_is_view = is_type_a_view(c_type)
|
||||
self.implement_regular_type_converter = self.c_type_is_view # by default, it will also make the converter of the associated regular type
|
||||
if self.c_type_is_view : self.regular_type = 'typename ' + self.c_type + '::regular_type'
|
||||
self.py_type = py_type
|
||||
c_to_py_type[self.c_type] = self.py_type # register the name translation for the doc generation
|
||||
self.hdf5 = hdf5
|
||||
assert serializable in [None, "boost", "tuple"]
|
||||
self.serializable = serializable
|
||||
self.is_printable = is_printable
|
||||
self.iterator = None
|
||||
self.doc = doc
|
||||
self.methods = {}
|
||||
self.pure_python_methods= {}
|
||||
self.constructor = None
|
||||
self.members= []
|
||||
self.properties= []
|
||||
|
||||
# Init arithmetic
|
||||
# expect a tuple : "algebra", "scalar1", "scalar2", etc...
|
||||
self.number_protocol = {}
|
||||
if arithmetic :
|
||||
abelian_group = arithmetic[0] in ("algebra", "abelian_group", "vector_space")
|
||||
vector_space = arithmetic[0] in ("algebra", "vector_space")
|
||||
algebra = arithmetic[0] in ("algebra")
|
||||
if abelian_group :
|
||||
# add
|
||||
add = pyfunction(py_name ="__add__")
|
||||
add.arity = 2
|
||||
add.add_overload (calling_pattern = "+", args = [(self.c_type,'x'), (self.c_type,'y')], rtype = self.c_type)
|
||||
self.number_protocol['add'] = add
|
||||
#sub
|
||||
sub = pyfunction(py_name ="__sub__")
|
||||
sub.arity = 2
|
||||
sub.add_overload (calling_pattern = "-", args = [(self.c_type,'x'), (self.c_type,'y')], rtype = self.c_type)
|
||||
self.number_protocol['subtract'] = sub
|
||||
if vector_space :
|
||||
# mul
|
||||
mul = pyfunction(py_name ="__mul__")
|
||||
mul.arity = 2
|
||||
for scalar in arithmetic[1:] :
|
||||
mul.add_overload (calling_pattern = "*", args = [(self.c_type,'x'), (scalar,'y')], rtype = self.c_type)
|
||||
mul.add_overload (calling_pattern = "*", args = [(scalar,'x'), (self.c_type,'y')], rtype = self.c_type)
|
||||
self.number_protocol['multiply'] = mul
|
||||
# div
|
||||
div = pyfunction(py_name ="__div__")
|
||||
div.arity = 2
|
||||
for scalar in arithmetic[1:] :
|
||||
div.add_overload (calling_pattern = "/", args = [(self.c_type,'x'), (scalar,'y')], rtype = self.c_type)
|
||||
self.number_protocol['divide'] = div
|
||||
if algebra :
|
||||
mul.add_overload (calling_pattern = "*", args = [(self.c_type,'x'), (self.c_type,'y')], rtype = self.c_type)
|
||||
|
||||
def prepare_for_generation(self) :
|
||||
# Called just before the code generation
|
||||
self.has_mapping_protocol = '__getitem__impl' in self.methods or '__len__impl' in self.methods
|
||||
if '__setitem__impl' in self.methods and not '__getitem__impl' in self.methods : raise RuntimeError, "Can not generate a class with a setter and no getter"
|
||||
|
||||
def add_method(self, py_name, **kw):
|
||||
"""
|
||||
Add a method name (or an overload of method name).
|
||||
All arguments passed by keywords to cfunction construction
|
||||
"""
|
||||
f = cfunction(is_method = True, **kw)
|
||||
if py_name not in self.methods : self.methods[py_name] = pyfunction(py_name = py_name, is_method = True, **kw)
|
||||
self.methods[py_name].overloads.append(f)
|
||||
|
||||
def add_iterator(self, **kw) :
|
||||
self.iterator = iterator_(**kw)
|
||||
|
||||
def add_pure_python_method(self, f, py_name = None):
|
||||
"""
|
||||
Add a method name (or an overload of method name).
|
||||
f can be :
|
||||
- a string module1.module2.fnt_name
|
||||
- a function in python...
|
||||
"""
|
||||
def process_doc(doc) :
|
||||
return doc.replace('\n','\\n') if doc else ''
|
||||
if type(f) ==type('') :
|
||||
module, name = f.rsplit('.',1)
|
||||
try :
|
||||
m = __import__(module.rsplit('.')[-1])
|
||||
doc = m.__dict__[name].__doc__
|
||||
except :
|
||||
raise
|
||||
self.pure_python_methods[py_name or name] = pure_pyfunction_from_module(py_name = name, module = module), 'module', process_doc(doc)
|
||||
elif callable(f) :
|
||||
assert py_name == None
|
||||
self.hidden_python_function[f.__name__] = f
|
||||
self.pure_python_methods[f.__name__] = f.__name__, 'inline', process_doc(f.__doc__)
|
||||
else : raise ValueError, "argument f must be callable or a string"
|
||||
|
||||
def add_constructor(self, build_from_regular_type = True, **kw):
|
||||
"""
|
||||
Add an overload of a constructor
|
||||
All arguments passed by keywords to function_ construction
|
||||
"""
|
||||
assert 'c_name' not in kw, "No c_name here"
|
||||
assert 'calling_pattern' not in kw, "No calling_pattern here"
|
||||
f = cfunction(c_name = "__init__", is_constructor = True, **kw)
|
||||
build_type = regular_type_if_view_else_type(self.c_type) if self.c_type_is_view and build_from_regular_type else self.c_type
|
||||
all_args = ",".join([ ('*' if t in module_.wrapped_types else '') + n for t,n,d in f.args])
|
||||
#all_args = ",".join([ ('*' if t in module_.wrapped_types else '') + n + (' = ' + d if d else '') for t,n,d in f.args])
|
||||
f._calling_pattern = "((%s *)self)->_c ->"%self.py_type + ('operator =' if not self.c_type_is_view else 'rebind') + " (%s (%s));"%(build_type,all_args)
|
||||
if not self.constructor :
|
||||
self.constructor = pyfunction(py_name = "__init__", **kw)
|
||||
self.constructor.is_constructor = True
|
||||
self.constructor.overloads.append(f)
|
||||
|
||||
def add_member(self, **kw):
|
||||
"""
|
||||
Add a class member
|
||||
All arguments passed by keywords to function_ construction
|
||||
"""
|
||||
m = member_(**kw)
|
||||
self.members.append(m)
|
||||
|
||||
def add_property(self, getter, setter = None, name = None, doc = ''):
|
||||
"""
|
||||
Add a property
|
||||
"""
|
||||
if not isinstance(getter, str) : getter.is_method = True
|
||||
self.properties.append( property_(name = name or getter.c_name, getter = getter, setter = setter, doc = doc) )
|
||||
|
||||
def add_call(self, **kw) :
|
||||
"""
|
||||
Add a the __call__ operator
|
||||
"""
|
||||
if 'c_name' not in kw and 'calling_pattern' not in kw : kw['c_name']= "operator()"
|
||||
self.add_method(py_name = "__call__", **kw)
|
||||
|
||||
def add_len(self, c_name = None, calling_pattern = None, doc = "Length") :
|
||||
"""
|
||||
Add the len operator
|
||||
"""
|
||||
if not c_name and not calling_pattern : c_name = "size"
|
||||
self.add_method(py_name = "__len__impl", c_name = c_name, calling_pattern = calling_pattern, signature="int()", doc= doc)
|
||||
self.methods['__len__impl'].do_implement = False # do not implement automatically, the signature is special
|
||||
|
||||
def add_getitem(self, signature, c_name = None, calling_pattern = None, doc = "operator[]" ) :
|
||||
"""
|
||||
Add a the __getitem__ operator
|
||||
"""
|
||||
assert not(c_name and calling_pattern)
|
||||
if calling_pattern :
|
||||
self.add_method(py_name = "__getitem__impl", calling_pattern = calling_pattern, doc = doc, signature = signature)
|
||||
else :
|
||||
self.add_method(py_name = "__getitem__impl", c_name = c_name or "operator[]", doc = doc, signature = signature)
|
||||
|
||||
def add_setitem(self, signature, calling_pattern = None, doc = "operator[]", **d ) :
|
||||
"""
|
||||
Add a the __setitem__ operator
|
||||
"""
|
||||
self.add_method(py_name = "__setitem__impl", calling_pattern = calling_pattern or "self_c[i] = v", doc = doc, signature = signature, **d)
|
||||
|
||||
def add_method_copy(self) :
|
||||
"""Add a method copy, that make a DEEP copy, using triqs make_clone"""
|
||||
self.add_method(py_name = "copy", calling_pattern = self.c_type + " result = make_clone(self_c)", signature = self.c_type +"()", doc = "Make a copy (clone) of self")
|
||||
|
||||
def add_method_copy_from(self) :
|
||||
"""Add a copy_from, using C++ assignment"""
|
||||
# other by pointer, it is necessarly a wrapped type
|
||||
self.add_method(py_name = "copy_from", calling_pattern = " self_c = *other", signature = 'void(' + self.c_type +" other)", doc = "Assignment")
|
||||
|
||||
class enum_ :
|
||||
"""
|
||||
Representation of an enum
|
||||
Data :
|
||||
- c_name : name in C
|
||||
- values : list of string representing the enumerated
|
||||
- doc : the doc string.
|
||||
"""
|
||||
def __init__(self, c_name, values, doc = '') :
|
||||
self.c_name, self.values, self.doc = c_name, values, doc
|
||||
|
||||
class module_ :
|
||||
"""
|
||||
Representation of a module
|
||||
Data :
|
||||
- name : name of the module
|
||||
- imported_modules : name of the modules to import (with their wrapped type converters)
|
||||
- doc : the doc string.
|
||||
- classes : dict : string -> class_. Key is the Python type
|
||||
- c_types : dict : string -> string. Correspondance Python type -> C++ type
|
||||
- functions : dict : string -> function_. Modules functions. Key is the python name.
|
||||
- include_list : a list of files to include for compilation
|
||||
"""
|
||||
wrapped_types = {}
|
||||
|
||||
def __init__(self, full_name, imported_modules = (), doc = '') :
|
||||
self.full_name = full_name
|
||||
self.name = full_name.rsplit('.',1)[-1]
|
||||
self.doc = doc
|
||||
self.classes = {}
|
||||
self.functions = {}
|
||||
self.include_list = []
|
||||
self.wrapped_types_by_me = {}
|
||||
self.imported_wrapped_types = {}
|
||||
self.enums = []
|
||||
self.using =[]
|
||||
self.python_functions = {}
|
||||
self.hidden_python_functions = {}
|
||||
|
||||
assert type(imported_modules) in [type(()), type([])]
|
||||
for m_desc in imported_modules:
|
||||
m = importlib.import_module("%s_desc"%m_desc)
|
||||
self.imported_wrapped_types[m_desc] = m.module.wrapped_types
|
||||
|
||||
def add_class(self, cls):
|
||||
if cls.py_type in self.classes : raise IndexError, "The class %s already exists"%cls.py_type
|
||||
self.classes[cls.py_type] = cls
|
||||
self.wrapped_types[cls.c_type] = cls.py_type
|
||||
self.wrapped_types_by_me[cls.c_type] = cls.py_type
|
||||
|
||||
def add_function(self, **kw):
|
||||
if "name" in kw :
|
||||
assert "py_name" not in kw and "c_name" not in kw, "name or c_name,py_name"
|
||||
name = kw.pop("name")
|
||||
kw["py_name"] = name
|
||||
kw["c_name"] = name
|
||||
py_name = kw["py_name"]
|
||||
f = cfunction(**kw)
|
||||
if py_name not in self.functions : self.functions[py_name] = pyfunction(**kw)
|
||||
self.functions[py_name].overloads.append(f)
|
||||
|
||||
def add_python_function(self, f, name = None, hidden = False) :
|
||||
assert callable(f)
|
||||
if not hidden :
|
||||
self.python_functions[name or f.__name__] = python_function(name or f.__name__, f)
|
||||
else :
|
||||
self.hidden_python_functions[name or f.__name__] = python_function(name or f.__name__, f)
|
||||
|
||||
def add_include(self, *path) :
|
||||
self.include_list.extend(path)
|
||||
|
||||
def add_using(self,ns) :
|
||||
self.using.append(ns)
|
||||
|
||||
def add_enum(self,**kw) :
|
||||
self.enums.append( enum_(**kw))
|
||||
|
||||
def get_proper_converter(self, t) :
|
||||
if t in basic_types_formatting : return ''
|
||||
if t in self.wrapped_types : return ',converter_for_parser_wrapped_type<'+t+'>'
|
||||
if t.split('<',1)[0].endswith("_view") : return ',converter_for_parser_view_type<'+t+'>'
|
||||
return ',converter_for_parser_non_wrapped_type<'+t+'>'
|
||||
|
||||
def all_args_kw_functions(self) :
|
||||
l = [ (f, self.name, None) for f in self.functions.values()]
|
||||
for c in self.classes.values() :
|
||||
l += [(m,c.py_type, c.c_type) for m in c.methods.values() if m.do_implement]
|
||||
if c.constructor :
|
||||
l.append( (c.constructor,c.py_type, c.c_type))
|
||||
return l
|
||||
|
||||
def prepare_for_generation(self) :
|
||||
for c in self.classes.values() : c.prepare_for_generation()
|
||||
for n,f in class_.hidden_python_function.items() :
|
||||
self.add_python_function(f,name = n, hidden=True)
|
||||
|
||||
def generate_code(self, mako_template, wrap_file) :
|
||||
self.prepare_for_generation()
|
||||
tpl = Template(filename=mako_template)
|
||||
rendered = tpl.render(module=self, regular_type_if_view_else_type= regular_type_if_view_else_type, is_type_a_view = is_type_a_view)
|
||||
with open(wrap_file,'w') as f:
|
||||
f.write(rendered)
|
1041
pytriqs/wrap_generator/wrapper.mako.cpp
Normal file
1041
pytriqs/wrap_generator/wrapper.mako.cpp
Normal file
File diff suppressed because it is too large
Load Diff
17
pytriqs/wrap_test/CMakeLists.txt
Normal file
17
pytriqs/wrap_test/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
SET(PYTHON_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/__init__.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/aux.py
|
||||
)
|
||||
|
||||
# Install python sources
|
||||
install (FILES ${PYTHON_SOURCES} DESTINATION ${TRIQS_PYTHON_LIB_DEST}/wrap_test)
|
||||
|
||||
# Build C extension module
|
||||
triqs_python_extension(my_module wrap_test)
|
||||
# ??triqs_set_rpath_for_target(my_module)
|
||||
|
||||
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
|
||||
|
||||
install (FILES ${CMAKE_SOURCE_DIR}/pytriqs/__init__.py.template DESTINATION "include/pytriqs/gf/local" RENAME __init__.py)
|
||||
|
3
pytriqs/wrap_test/__init__.py
Normal file
3
pytriqs/wrap_test/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# just used in installing pxd files
|
||||
__all__=[]
|
||||
|
193
pytriqs/wrap_test/a.hpp
Normal file
193
pytriqs/wrap_test/a.hpp
Normal file
@ -0,0 +1,193 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <ostream>
|
||||
#include <triqs/arrays.hpp>
|
||||
//#include <triqs/h5.hpp>
|
||||
|
||||
namespace triqs { namespace py_tools {
|
||||
class reductor;
|
||||
class reconstructor;
|
||||
}}
|
||||
|
||||
struct A {
|
||||
private :
|
||||
double pp =90;
|
||||
public:
|
||||
|
||||
/// Doc of i member
|
||||
int i = 1;
|
||||
/// Doc of x,y
|
||||
double x = 3, y = 89;
|
||||
|
||||
A() = default;
|
||||
A(A&&) = default;
|
||||
A(A const &) = delete; // to prevent using copy
|
||||
A &operator =(A&&) = default;
|
||||
A &operator =(A const &) = default;
|
||||
|
||||
std::vector<int> data = { 1, 2, 3, 4, 5, 6 };
|
||||
|
||||
using const_iterator = std::vector<int>::const_iterator;
|
||||
const_iterator begin() const { return data.begin(); }
|
||||
const_iterator end() const { return data.end(); }
|
||||
|
||||
/// The nice doc of m1 ....
|
||||
double m1(int u, double y) const {
|
||||
std::cout << " calling m1 " << u << " " << y << std::endl;
|
||||
return x + 2 * u + 3 * y;
|
||||
}
|
||||
|
||||
|
||||
double m1(int u) const {
|
||||
std::cout << " calling m1 one arg " << u << std::endl;
|
||||
return x + 2 * u;
|
||||
}
|
||||
|
||||
/// m2 has also some doc !
|
||||
double m2(double u) const {
|
||||
std::cout << " calling m2 one arg " << u << std::endl;
|
||||
return x + 20 * u;
|
||||
}
|
||||
|
||||
double &operator[](int i) { return y; }
|
||||
|
||||
A operator+(A const &y) {
|
||||
A a;
|
||||
a.x = x + y.x;
|
||||
return a;
|
||||
}
|
||||
A operator-(A const &y) {
|
||||
A a;
|
||||
a.x = x - y.x;
|
||||
return a;
|
||||
}
|
||||
A operator*(A const &y) {
|
||||
A a;
|
||||
a.x = x * y.x;
|
||||
return a;
|
||||
}
|
||||
friend A operator*(A const &x, double s) {
|
||||
A a;
|
||||
a.x = x.x * s;
|
||||
return a;
|
||||
}
|
||||
friend A operator*(double s, A const &x) {
|
||||
A a;
|
||||
a.x = x.x * s;
|
||||
return a;
|
||||
}
|
||||
A operator/(A const &y) {
|
||||
A a;
|
||||
a.x = x / y.x;
|
||||
return a;
|
||||
}
|
||||
A operator/(double y) {
|
||||
A a;
|
||||
a.x = x / y;
|
||||
return a;
|
||||
}
|
||||
|
||||
int _get_i() const { return i; }
|
||||
void _set_i(int j) { i = j; }
|
||||
|
||||
int operator()(int u) {
|
||||
std::cout << " Calling with u = " << u << std::endl;
|
||||
return u + i;
|
||||
}
|
||||
|
||||
friend void h5_write(int gr, A const &a) {
|
||||
std::cout << " mimic h5 writing" << std::endl;
|
||||
}
|
||||
|
||||
/// Write into HDF5
|
||||
friend void h5_write(triqs::h5::group fg, std::string subgroup_name, A const &a) {
|
||||
auto gr = fg.create_group(subgroup_name);
|
||||
h5_write(gr,"x",a.x);
|
||||
}
|
||||
|
||||
/// Read from HDF5
|
||||
friend void h5_read(triqs::h5::group fg, std::string subgroup_name, A & a) {
|
||||
auto gr = fg.open_group(subgroup_name);
|
||||
//TRIQS_RUNTIME_ERROR << "A nasty error in h5read ....";
|
||||
h5_read(gr,"x",a.x);
|
||||
}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &out, A const &a) {
|
||||
return out << " I am an A with x= " << a.x;
|
||||
}
|
||||
|
||||
friend class triqs::py_tools::reductor;
|
||||
friend class triqs::py_tools::reconstructor;
|
||||
/// boost serialisation
|
||||
template <typename Archive>
|
||||
void serialize(Archive &ar, const unsigned int version) {
|
||||
ar & i & x;
|
||||
}
|
||||
};
|
||||
|
||||
/// some function using A
|
||||
void print_a(A const & a) {
|
||||
std::cout << "my a is " << a.x << std::endl;
|
||||
}
|
||||
|
||||
/// function with an error
|
||||
void print_err(A const &a) {
|
||||
TRIQS_RUNTIME_ERROR << "hum does not look good" << a;
|
||||
}
|
||||
|
||||
/// DOC make_vector...
|
||||
std::vector<int> make_vector(int size) {
|
||||
std::vector<int> r(size, 0);
|
||||
for (int i = 0; i < size; ++i)
|
||||
r[i] = i;
|
||||
return r;
|
||||
}
|
||||
|
||||
std::vector<std::vector<int> > make_vector2(int size) {
|
||||
return { make_vector(size), make_vector(size + 1) };
|
||||
}
|
||||
|
||||
std::vector<int> vector_x2(std::vector<int> const &v) {
|
||||
auto r = v;
|
||||
for (auto &x : r)
|
||||
x *= 2;
|
||||
return r;
|
||||
}
|
||||
|
||||
using triqs::arrays::matrix;
|
||||
using triqs::arrays::array;
|
||||
using triqs::arrays::range;
|
||||
using namespace triqs::arrays;
|
||||
|
||||
void iter_on_range(range r){
|
||||
foreach(r, [](int i) { std::cout << i << std::endl;});
|
||||
}
|
||||
|
||||
matrix<double> make_matrix(int size) { return make_unit_matrix<double>(size); }
|
||||
|
||||
void print_matrix(matrix<double> const &M) {
|
||||
// std::cout << M <<std::endl;
|
||||
}
|
||||
|
||||
|
||||
std::function<int(int,int)> make_fnt_ii() {
|
||||
return [](int i, int j) { return i + 2*j;};
|
||||
}
|
||||
|
||||
std::function<int(int,int,double)> make_fnt_iid() {
|
||||
return [](int i, int j, double a) { return a+ i + 2*j;};
|
||||
}
|
||||
|
||||
|
||||
void use_fnt_ii(std::function<int(int,int)> f) {
|
||||
std::cout << "use_fnt ii \n"<< f(1,2) << std::endl;
|
||||
}
|
||||
|
||||
void use_fnt_iid(std::function<int(int,int,double)> f) {
|
||||
std::cout << "use_fnt iid \n"<< f(1,2,4.5) << std::endl;
|
||||
}
|
||||
|
||||
|
||||
std::function<void(int,int)> make_fnt_void() {
|
||||
return [](int i, int j) { std::cout << " I am a C++ lambda : "<<i <<" "<< j << std::endl;};
|
||||
}
|
21
pytriqs/wrap_test/aux.py
Normal file
21
pytriqs/wrap_test/aux.py
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
def ffg( *args, **kw) :
|
||||
""" my doc of ffg in module """
|
||||
print "calling ffg, with :"
|
||||
print args
|
||||
print kw
|
||||
#return [2*x for x in args], kw
|
||||
return tuple(2*x for x in args), kw
|
||||
|
||||
|
||||
def post1(res) :
|
||||
return [res]
|
||||
|
||||
|
||||
def pure_py1(self, i) :
|
||||
"""
|
||||
doc of pure_py1 : a nice funciton ,...
|
||||
"""
|
||||
i = i/2
|
||||
print " I am in pure python method pure_py1 %s "%i
|
||||
return ["pure_py1 return list"]
|
6
pytriqs/wrap_test/b.hpp
Normal file
6
pytriqs/wrap_test/b.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "./a.hpp"
|
||||
|
||||
/// some function using A
|
||||
void print_a2(A const & a) {
|
||||
std::cout << "module B : my a is " << a.x << std::endl;
|
||||
}
|
10
pytriqs/wrap_test/my_moduleB_desc.py
Normal file
10
pytriqs/wrap_test/my_moduleB_desc.py
Normal file
@ -0,0 +1,10 @@
|
||||
from wrap_generator import *
|
||||
|
||||
# The module
|
||||
mod = module_(full_name = "pytriqs.wrap_test.my_moduleB", imported_modules = ["my_module"], doc = " Doc of my_module ")
|
||||
mod.add_include("../c++/b.hpp") # FIX PUT ASOLUTE NAME
|
||||
|
||||
mod.add_function (name = "print_a2", signature = "void(A a)", doc = "DOC of print_a")
|
||||
|
||||
mod.generate_code(mako_template = sys.argv[1], wrap_file = sys.argv[2])
|
||||
|
104
pytriqs/wrap_test/my_module_desc.py
Normal file
104
pytriqs/wrap_test/my_module_desc.py
Normal file
@ -0,0 +1,104 @@
|
||||
from wrap_generator import *
|
||||
|
||||
# The module
|
||||
module = module_(full_name = "pytriqs.wrap_test.my_module", doc = " Doc of my_module ")
|
||||
module.add_include("./a.hpp")
|
||||
module.add_include("<triqs/arrays.hpp>")
|
||||
|
||||
# one class
|
||||
g = class_(
|
||||
py_type = "Ac",
|
||||
c_type = "A",
|
||||
#serializable= "boost",
|
||||
serializable= "tuple",
|
||||
is_printable= True,
|
||||
hdf5 = True,
|
||||
arithmetic = ("algebra","double")
|
||||
)
|
||||
|
||||
# add a constructor
|
||||
g.add_constructor(doc = "DOC of constructor", args = [])
|
||||
|
||||
# add a method m1, with 3 overloads in C++ : dispatch is done on the type of the arguments
|
||||
g.add_method(py_name = "m1", c_name = "m1", signature = "double (int u, double y = 3)", doc = "DOC of m1")
|
||||
g.add_method(py_name = "m1", c_name = "m1", signature = "double (int u)", doc = "DOC of m1...")
|
||||
g.add_method(py_name = "m1", c_name = "m2", signature = "double (double u)", doc = "DOC of m1...")
|
||||
|
||||
# another version of the method, with some pre/post processing written in python
|
||||
g.add_method(py_name = "m1p", c_name = "m1", signature = "double (int u, double y = 3)", doc = "DOC of mm", python_precall = "pytriqs.wrap_test.aux.ffg", python_postcall = "pytriqs.wrap_test.aux.post1")
|
||||
|
||||
# demo of adding a simple piece of C++ code, there is no C++ method corresponding
|
||||
g.add_method(py_name = "m1_x", calling_pattern = "bool result = (self_c.x >0) && (self_c.x < 10)" , signature = "bool()", doc = "A method which did not exist in C++")
|
||||
|
||||
# alternative syntax
|
||||
#g.add_method(py_name = "m1", python_precall = "aux.ffg", python_postcall = "aux.post1").add_overload(c_name = "m1", rtype = "double", doc = "DOC of mm", args = [("int","u"), ("double","y",3)])
|
||||
|
||||
# older syntax, giving rtype and args (better for automatic scripts).
|
||||
g.add_method(py_name = "m1f", c_name = "m1", rtype = "double", doc = "DOC of mm", args = [("int","u"), ("double","y",3)])
|
||||
|
||||
# add the call operator
|
||||
g.add_call(signature = "int(int u)", doc = "call op")
|
||||
|
||||
# add getitem/setitem ...
|
||||
g.add_getitem(signature = "double(int i)", doc = " doc [] ")
|
||||
g.add_setitem(signature = "void(int i, double v)", doc = " doc [] set ")
|
||||
|
||||
# We can also add the call to a pure python function !
|
||||
g.add_pure_python_method("pytriqs.wrap_test.aux.pure_py1")
|
||||
|
||||
def ffg2(self, *args, **kw) :
|
||||
""" my doc of the function ffg2 """
|
||||
print "calling ffg2 [inline], with :"
|
||||
print args
|
||||
print kw
|
||||
#return [2*x for x in args], kw
|
||||
print dir()
|
||||
return tuple(2*x for x in args), kw
|
||||
|
||||
g.add_pure_python_method(ffg2)
|
||||
|
||||
# public members -> as properties
|
||||
g.add_member(c_name = "x", c_type = "double", doc = "x field of A ....")
|
||||
g.add_member(c_name = "y", c_type = "double", doc = "y field of A : read_only", read_only=True)
|
||||
|
||||
# properties : transform a couple of methods into properties
|
||||
g.add_property(name = "i", getter = cfunction(c_name="_get_i", doc = "i prop get doc", signature = "int()"),
|
||||
setter = cfunction(c_name="_set_i", doc = "i prop set doc", signature = "void(int j)"))
|
||||
|
||||
g.add_property(name = "ii", getter = cfunction(c_name="_get_i", doc = "i prop get doc", signature = "int()"))
|
||||
|
||||
g.add_iterator()
|
||||
|
||||
module.add_class(g)
|
||||
|
||||
# various module functions....
|
||||
module.add_function (name = "print_a", signature = "void(A a)", doc = "DOC of print_a")
|
||||
module.add_function (name = "print_err", signature = "void(A a)", doc = "DOC of print_a")
|
||||
module.add_function (name = "make_vector", signature = "std::vector<int>(int size)", doc = "DOC of print_a")
|
||||
module.add_function (name = "make_vector2", signature = "std::vector<std::vector<int>>(int size)", doc = "DOC ....")
|
||||
module.add_function (name = "vector_x2", signature = "std::vector<int>(std::vector<int> v)", doc = "DOC of print_a")
|
||||
|
||||
module.add_function (name = "make_matrix", signature = "matrix_view<double>(int size)", doc = "DOC ....")
|
||||
|
||||
module.add_function (name = "iter_on_range", signature = "void (range r)" , doc = "DOC ....")
|
||||
|
||||
module.add_function (name = "make_fnt_ii", rtype = "std::function<int(int,int)>", doc = "....", args = [])
|
||||
module.add_function (name = "make_fnt_iid", rtype = "std::function<int(int,int,double)>", doc = "....", args = [])
|
||||
module.add_function (name = "make_fnt_void", rtype = "std::function<void(int,int)>", doc = "....", args = [])
|
||||
|
||||
module.add_function (name = "use_fnt_ii", signature = "void(std::function<int(int,int)> f)", doc = "....")
|
||||
module.add_function (name = "use_fnt_iid", signature = "void(std::function<int(int,int,double)> f)", doc = "....")
|
||||
|
||||
def f1(x,y):
|
||||
print " I am in f1 ", x,y
|
||||
print y + 1/0.2
|
||||
print tuple([x])
|
||||
assert x>0, "an horrible error"
|
||||
|
||||
# The code of f1 will be copied verbatim in the module, and compiled at the
|
||||
# initialisation of the module
|
||||
module.add_python_function(f1)
|
||||
|
||||
if __name__ == '__main__' :
|
||||
module.generate_code(mako_template = sys.argv[1], wrap_file = sys.argv[2])
|
||||
|
553
triqs/python_tools/wrapper_tools.hpp
Normal file
553
triqs/python_tools/wrapper_tools.hpp
Normal file
@ -0,0 +1,553 @@
|
||||
#pragma once
|
||||
#include <Python.h>
|
||||
#include "structmember.h"
|
||||
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-writable-strings"
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-writable-strings"
|
||||
|
||||
// I can use the trace in triqs::exception
|
||||
#define CATCH_AND_RETURN(MESS,RET)\
|
||||
catch(triqs::exception const & e) {\
|
||||
auto err = std::string("Error " MESS "\nC++ error was : \n") + e.what();\
|
||||
PyErr_SetString(PyExc_RuntimeError, err.c_str());\
|
||||
return RET; }\
|
||||
catch(std::exception const & e) {\
|
||||
auto err = std::string("Error " MESS "\nC++ error was : \n") + e.what();\
|
||||
PyErr_SetString(PyExc_RuntimeError, err.c_str());\
|
||||
return RET; }\
|
||||
catch(...) { PyErr_SetString(PyExc_RuntimeError,"Unknown error " MESS ); return RET; }\
|
||||
|
||||
namespace triqs { namespace py_tools {
|
||||
|
||||
//--------------------- pyref -----------------------------
|
||||
|
||||
// a little class to hold an owned ref, make sure it is decref at desctruction
|
||||
// with some useful factories.
|
||||
class pyref {
|
||||
PyObject * ob = NULL;
|
||||
public:
|
||||
pyref() = default;
|
||||
pyref(PyObject *new_ref) : ob(new_ref){}
|
||||
~pyref() { Py_XDECREF(ob);}
|
||||
operator PyObject *() const { return ob;}
|
||||
PyObject * new_ref() const { Py_XINCREF(ob); return ob;}
|
||||
explicit operator bool() const { return (ob!=NULL);}
|
||||
bool is_null() const { return ob==NULL;}
|
||||
bool is_None() const { return ob==Py_None;}
|
||||
pyref attr(const char * s) { return (ob ? PyObject_GetAttrString(ob,s) : NULL);} // NULL : pass the error in chain call x.attr().attr()....
|
||||
pyref operator()(PyObject * a1) { return (ob ? PyObject_CallFunctionObjArgs(ob,a1,NULL) : NULL);} // NULL : pass the error in chain call x.attr().attr()....
|
||||
pyref operator()(PyObject * a1,PyObject * a2) { return (ob ? PyObject_CallFunctionObjArgs(ob,a1,a2,NULL) : NULL);} // NULL : pass the error in chain call x.attr().attr()....
|
||||
pyref(pyref const&p) {ob = p.ob; Py_XINCREF(ob);}
|
||||
pyref(pyref && p){ ob = p.ob; p.ob=NULL;}
|
||||
pyref& operator =(pyref const&) = delete;
|
||||
pyref& operator =(pyref &&p) {ob = p.ob; p.ob=NULL; return *this;}
|
||||
// factories. No public constructor.
|
||||
// The point is that PyObject is borrowed or new, depending on the function that produced it
|
||||
// Need to check in the API doc for each case.
|
||||
static pyref module(std::string const &module_name) { return PyImport_ImportModule(module_name.c_str()); }
|
||||
static pyref string(std::string const &s) { return PyString_FromString(s.c_str());}
|
||||
};
|
||||
|
||||
pyref borrowed(PyObject * ob) { Py_XINCREF(ob); return {ob};}
|
||||
|
||||
//--------------------- py_converters -----------------------------
|
||||
|
||||
template<typename T> struct py_converter {
|
||||
static PyObject * c2py(T const & x);
|
||||
static T py2c(PyObject * ob);
|
||||
static bool is_convertible(PyObject * ob, bool raise_exception);
|
||||
};
|
||||
|
||||
// We only use these functions in the code, not converter
|
||||
// TODO : Does c2py return NULL in failure ? Or is it undefined...
|
||||
template <typename T> PyObject *convert_to_python(T &&x) {
|
||||
return py_converter<typename std::decay<T>::type>::c2py(std::forward<T>(x));
|
||||
}
|
||||
// can convert_from_python raise a triqs exception ? NO
|
||||
template<typename T> auto convert_from_python(PyObject * ob) DECL_AND_RETURN(py_converter<T>::py2c(ob));
|
||||
template <typename T> bool convertible_from_python(PyObject *ob, bool raise_exception) {
|
||||
return py_converter<T>::is_convertible(ob, raise_exception);
|
||||
}
|
||||
|
||||
// details
|
||||
template <bool B> struct _bool {};
|
||||
template <typename T> struct _is_pointer : _bool<false> {};
|
||||
template <typename T> struct _is_pointer<T *> : _bool<true> {};
|
||||
template <> struct _is_pointer<PyObject *> : _bool<false> {}; // yes, false, it is a special case...
|
||||
|
||||
// adapter needed for parsing with PyArg_ParseTupleAndKeywords later in the functions
|
||||
template <typename T> static int converter_for_parser_(PyObject *ob, T *p, _bool<false>) {
|
||||
if (!py_converter<T>::is_convertible(ob, true)) return 0;
|
||||
*p = std::move(convert_from_python<T>(ob)); // non wrapped types are converted to values, they can be moved !
|
||||
return 1;
|
||||
}
|
||||
template <typename T> static int converter_for_parser_(PyObject *ob, T **p, _bool<true>) {
|
||||
if (!convertible_from_python<T>(ob)) return 0;
|
||||
*p = &(convert_from_python<T>(ob));
|
||||
return 1;
|
||||
}
|
||||
template <typename T> static int converter_for_parser(PyObject *ob, T *p) {
|
||||
return converter_for_parser_(ob, p, _is_pointer<T>());
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
// Tools for the implementation of reduce (V2)
|
||||
// -----------------------------------
|
||||
|
||||
// auxiliary object to reduce the object into a tuple
|
||||
class reductor {
|
||||
std::vector<PyObject *> elem;
|
||||
PyObject *as_tuple() {
|
||||
int l = elem.size();
|
||||
PyObject *tup = PyTuple_New(l);
|
||||
for (int pos = 0; pos < l; ++pos) PyTuple_SetItem(tup, pos, elem[pos]);
|
||||
return tup;
|
||||
}
|
||||
public:
|
||||
template <typename T> reductor & operator&(T &x) { elem.push_back(convert_to_python(x)); return *this;}
|
||||
template<typename T>
|
||||
PyObject * apply_to(T & x) { x.serialize(*this,0); return as_tuple();}
|
||||
};
|
||||
|
||||
// inverse : auxiliary object to reconstruct the object from the tuple ...
|
||||
class reconstructor {
|
||||
PyObject * tup; // borrowed ref
|
||||
int pos=0, pos_max = 0;
|
||||
public:
|
||||
reconstructor(PyObject *borrowed_ref) : tup(borrowed_ref) { pos_max = PyTuple_Size(tup)-1;}
|
||||
template <typename T> reconstructor &operator&(T &x) {
|
||||
if (pos > pos_max) TRIQS_RUNTIME_ERROR << " Tuple too short in reconstruction";
|
||||
x = convert_from_python<T>(PyTuple_GetItem(tup, pos++));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// no protection for convertion !
|
||||
template <typename T> struct py_converter_from_reductor {
|
||||
template<typename U> static PyObject *c2py(U && x) { return reductor{}.apply_to(x); }
|
||||
static T py2c(PyObject *ob) {
|
||||
T res;
|
||||
auto r = reconstructor{ob};
|
||||
res.serialize(r, 0);
|
||||
return res;
|
||||
}
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) { return true;}
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
// basic types
|
||||
// -----------------------------------
|
||||
|
||||
// PyObject *
|
||||
template <> struct py_converter<PyObject *> {
|
||||
static PyObject *c2py(PyObject *ob) { return ob; }
|
||||
static PyObject *py2c(PyObject *ob) { return ob; }
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) { return true;}
|
||||
};
|
||||
|
||||
// --- bool
|
||||
template <> struct py_converter<bool> {
|
||||
static PyObject *c2py(bool b) {
|
||||
if (b)
|
||||
Py_RETURN_TRUE;
|
||||
else
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
static bool py2c(PyObject *ob) { return ob == Py_True; }
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
if (PyBool_Check(ob)) return true;
|
||||
if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Can not convert to bool");}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// --- long
|
||||
|
||||
template <> struct py_converter<long> {
|
||||
static PyObject *c2py(long i) { return PyInt_FromLong(i); }
|
||||
static long py2c(PyObject *ob) { return PyInt_AsLong(ob); }
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
if (PyInt_Check(ob)) return true;
|
||||
if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Can not convert to long");}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct py_converter<int> : py_converter<long> {};
|
||||
template <> struct py_converter<size_t> : py_converter<long> {};
|
||||
|
||||
// --- double
|
||||
|
||||
template <> struct py_converter<double> {
|
||||
static PyObject *c2py(double x) { return PyFloat_FromDouble(x); }
|
||||
static double py2c(PyObject *ob) { return PyFloat_AsDouble(ob); }
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
if (PyFloat_Check(ob) || PyInt_Check(ob)) return true;
|
||||
if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Can not convert to double");}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// --- complex
|
||||
|
||||
template <> struct py_converter<std::complex<double>> {
|
||||
static PyObject *c2py(std::complex<double> x) { return PyComplex_FromDoubles(x.real(), x.imag()); }
|
||||
static std::complex<double> py2c(PyObject *ob) {
|
||||
auto r = PyComplex_AsCComplex(ob);
|
||||
return {r.real, r.imag};
|
||||
}
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
if (PyComplex_Check(ob)) return true;
|
||||
if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Can not convert to complex");}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// --- string
|
||||
|
||||
template <> struct py_converter<std::string> {
|
||||
static PyObject *c2py(std::string const &x) { return PyString_FromString(x.c_str()); }
|
||||
static std::string py2c(PyObject *ob) { return PyString_AsString(ob); }
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
if (PyString_Check(ob)) return true;
|
||||
if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Can not convert to string");}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// --- h5 group of h5py into a triqs::h5 group
|
||||
|
||||
template <> struct py_converter<triqs::h5::group> {
|
||||
|
||||
static PyObject *c2py(std::string const &x) =delete;
|
||||
|
||||
static pyref group_type;
|
||||
|
||||
static triqs::h5::group py2c (PyObject * ob) {
|
||||
int id = PyInt_AsLong(borrowed(ob).attr("id").attr("id"));
|
||||
int cmp = PyObject_RichCompareBool((PyObject *)ob->ob_type, group_type, Py_EQ);
|
||||
return triqs::h5::group (id, (cmp==1));
|
||||
}
|
||||
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
if (group_type.is_null()) {
|
||||
group_type = pyref::module("h5py").attr("highlevel").attr("Group");
|
||||
if (PyErr_Occurred()) TRIQS_RUNTIME_ERROR << "Can not load h5py module and find the group in it";
|
||||
}
|
||||
int cmp = PyObject_RichCompareBool((PyObject *)ob->ob_type, group_type, Py_EQ);
|
||||
if (cmp<0) {
|
||||
if (raise_exception) {
|
||||
PyErr_SetString(PyExc_TypeError, "hd5 : internal : comparison to group type has failed !!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
pyref id_py = borrowed(ob).attr("id").attr("id");
|
||||
if ((!id_py) ||(!PyInt_Check((PyObject*)id_py))) {
|
||||
if (raise_exception) {
|
||||
PyErr_SetString(PyExc_TypeError, "hd5 : INTERNAL : group id.id is not an int !!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// --- vector ---
|
||||
|
||||
template <typename T> struct py_converter<std::vector<T>> {
|
||||
static PyObject * c2py(std::vector<T> const &v) {
|
||||
PyObject * list = PyList_New(0);
|
||||
for (auto const & x : v) if (PyList_Append(list, py_converter<T>::c2py(x)) == -1) { Py_DECREF(list); return NULL;} // error
|
||||
return list;
|
||||
}
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
if (!PySequence_Check(ob)) goto _false;
|
||||
{
|
||||
pyref seq = PySequence_Fast(ob, "expected a sequence");
|
||||
int len = PySequence_Size(ob);
|
||||
for (int i = 0; i < len; i++) if (!py_converter<T>::is_convertible(PySequence_Fast_GET_ITEM((PyObject*)seq, i),raise_exception)) goto _false; //borrowed ref
|
||||
return true;
|
||||
}
|
||||
_false:
|
||||
if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Can not convert to std::vector");}
|
||||
return false;
|
||||
}
|
||||
static std::vector<T> py2c(PyObject * ob) {
|
||||
pyref seq = PySequence_Fast(ob, "expected a sequence");
|
||||
std::vector<T> res;
|
||||
int len = PySequence_Size(ob);
|
||||
for (int i = 0; i < len; i++) res.push_back(py_converter<T>::py2c(PySequence_Fast_GET_ITEM((PyObject*)seq, i))); //borrowed ref
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
// in CPP file ?
|
||||
pyref py_converter<triqs::h5::group>::group_type;
|
||||
|
||||
// --- mini_vector<T,N>---
|
||||
// via std::vector
|
||||
template <typename T, int N> struct py_converter<triqs::utility::mini_vector<T,N>> {
|
||||
using conv = py_converter<std::vector<T>>;
|
||||
static PyObject * c2py(triqs::utility::mini_vector<T,N> const &v) {
|
||||
return conv::c2py( v.to_vector());
|
||||
}
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
return conv::is_convertible(ob,raise_exception);
|
||||
}
|
||||
static triqs::utility::mini_vector<T,N> py2c(PyObject * ob) {
|
||||
return conv::py2c(ob);
|
||||
}
|
||||
};
|
||||
|
||||
// --- array
|
||||
|
||||
template <typename ArrayType> struct py_converter_array {
|
||||
static PyObject *c2py(ArrayType const &x) { return x.to_python(); }
|
||||
static ArrayType py2c(PyObject *ob) {
|
||||
return ArrayType {ob};
|
||||
}
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
try {
|
||||
py2c(ob);
|
||||
return true;
|
||||
}
|
||||
catch (...) {
|
||||
if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Can not convert to array/matrix/vector");}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, int R> struct py_converter<triqs::arrays::array_view<T, R>> : py_converter_array<triqs::arrays::array_view<T, R>> {};
|
||||
template <typename T> struct py_converter<triqs::arrays::matrix_view<T>> : py_converter_array<triqs::arrays::matrix_view<T>> {};
|
||||
template <typename T> struct py_converter<triqs::arrays::vector_view<T>> : py_converter_array<triqs::arrays::vector_view<T>> {};
|
||||
|
||||
template <typename T, int R> struct py_converter<triqs::arrays::array<T, R>> : py_converter_array<triqs::arrays::array<T, R>> {};
|
||||
template <typename T> struct py_converter<triqs::arrays::matrix<T>> : py_converter_array<triqs::arrays::matrix<T>> {};
|
||||
template <typename T> struct py_converter<triqs::arrays::vector<T>> : py_converter_array<triqs::arrays::vector<T>> {};
|
||||
|
||||
// --- range
|
||||
|
||||
// convert from python slice and int (interpreted are slice(i,i+1,1))
|
||||
template <> struct py_converter<triqs::arrays::range> {
|
||||
static PyObject *c2py(triqs::arrays::range const &r) {
|
||||
return PySlice_New(convert_to_python(r.first()), convert_to_python(r.last()), convert_to_python(r.step()));
|
||||
}
|
||||
static triqs::arrays::range py2c(PyObject *ob) {
|
||||
if (PyInt_Check(ob)) {
|
||||
int i = PyInt_AsLong(ob);
|
||||
return {i,i+1,1};
|
||||
}
|
||||
int len = 4; // no clue what this len is ??
|
||||
Py_ssize_t start, stop, step, slicelength;
|
||||
if (PySlice_GetIndicesEx((PySliceObject *)ob, len, &start, &stop, &step, &slicelength) < 0) return {};
|
||||
return {start, stop, step};
|
||||
}
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
if (PySlice_Check(ob) || PyInt_Check(ob)) return true;
|
||||
if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Can not convert to python slice");}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// --- gf<U...> ----
|
||||
/*template <typename ...U > struct py_converter<triqs::gfs::gf<U...>> {
|
||||
using conv = py_converter<triqs::gfs::gf_view<U...>>;
|
||||
static PyObject *c2py(triqs::gfs::gf<U...> &g) { return conv::c2py(g); }
|
||||
static PyObject *c2py(triqs::gfs::gf<U...> &&g) { return conv::c2py(g); }
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
return conv::is_convertible(ob,raise_exception);
|
||||
}
|
||||
static triqs::gfs::gf<U...> py2c(PyObject *ob) { return conv::py2c(ob); }
|
||||
};
|
||||
*/
|
||||
|
||||
// --- nothing <--> None ----
|
||||
#ifdef TRIQS_GF_INCLUDED
|
||||
|
||||
template<> struct py_converter<triqs::gfs::nothing> {
|
||||
static PyObject *c2py(triqs::gfs::nothing g) { Py_RETURN_NONE;}
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
if (ob ==Py_None) return true;
|
||||
if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Can not convert to triqs::gfs::nothing : can only convert None");}
|
||||
return false;
|
||||
}
|
||||
static triqs::gfs::nothing py2c(PyObject *ob) { return {}; }
|
||||
};
|
||||
|
||||
|
||||
template<> struct py_converter<triqs::gfs::indices_2> : py_converter_from_reductor<triqs::gfs::indices_2>{};
|
||||
template<bool B> struct py_converter<triqs::gfs::matsubara_domain<B>> : py_converter_from_reductor<triqs::gfs::matsubara_domain<B>>{};
|
||||
template<> struct py_converter<triqs::gfs::R_domain> : py_converter_from_reductor<triqs::gfs::R_domain>{};
|
||||
|
||||
#endif
|
||||
|
||||
// ---- function ----
|
||||
|
||||
// a few useful meta tricks
|
||||
template <int N> struct _int {};
|
||||
template <int... N> struct index_seq {};
|
||||
template <typename U> struct nop {};
|
||||
|
||||
template <int N> struct _make_index_seq;
|
||||
template <int N> using make_index_seq = typename _make_index_seq<N>::type;
|
||||
template <> struct _make_index_seq<0> { using type = index_seq<>; };
|
||||
template <> struct _make_index_seq<1> { using type = index_seq<0>; };
|
||||
template <> struct _make_index_seq<2> { using type = index_seq<0, 1>; };
|
||||
template <> struct _make_index_seq<3> { using type = index_seq<0, 1, 2>; };
|
||||
template <> struct _make_index_seq<4> { using type = index_seq<0, 1, 2, 3>; };
|
||||
template <> struct _make_index_seq<5> { using type = index_seq<0, 1, 2, 3, 4>; };
|
||||
|
||||
template <int N> struct make_format { static const char * value;};
|
||||
template <> const char * make_format<0>::value = "";
|
||||
template <> const char * make_format<1>::value = "O&";
|
||||
template <> const char * make_format<2>::value = "O&O&";
|
||||
template <> const char * make_format<3>::value = "O&O&O&";
|
||||
template <> const char * make_format<4>::value = "O&O&O&O&";
|
||||
template <> const char * make_format<5>::value = "O&O&O&O&O&";
|
||||
|
||||
template <typename R, typename... T> struct py_converter<std::function<R(T...)>> {
|
||||
|
||||
static_assert(sizeof...(T) < 5, "More than 5 variables not implemented");
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
std::function<R(T...)> *_c;
|
||||
} std_function;
|
||||
|
||||
static PyObject* std_function_new (PyTypeObject *type, PyObject *args, PyObject *kwds) {
|
||||
std_function *self;
|
||||
self = (std_function *)type->tp_alloc(type, 0);
|
||||
if (self != NULL) {
|
||||
self->_c = new std::function<R(T...)>{};
|
||||
}
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
static void std_function_dealloc(std_function* self) {
|
||||
delete self->_c;
|
||||
self->ob_type->tp_free((PyObject*)self);
|
||||
}
|
||||
|
||||
// technical details to implement the __call function of the wrapping python object, cf below
|
||||
// we are using the unpack trick of the apply proposal for the C++ standard : cf XXXX
|
||||
//
|
||||
// specialise the convertion of the return type in the void case
|
||||
template <typename RR, typename TU, int... Is>
|
||||
static PyObject *_call_and_treat_return(nop<RR>, std_function *pyf, TU const &tu, index_seq<Is...>) {
|
||||
return py_converter<RR>::c2py(pyf->_c->operator()(std::get<Is>(tu)...));
|
||||
}
|
||||
template <typename TU, int... Is>
|
||||
static PyObject *_call_and_treat_return(nop<void>, std_function *pyf, TU const &tu, index_seq<Is...>) {
|
||||
pyf->_c->operator()(std::get<Is>(tu)...);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
using arg_tuple_t = std::tuple<T...>;
|
||||
using _int_max = _int<sizeof...(T) - 1>;
|
||||
|
||||
template <typename... U> static int _parse(_int<-1>, PyObject *args, arg_tuple_t &tu, U... u) {
|
||||
return PyArg_ParseTuple(args, make_format<sizeof...(T)>::value, u...);
|
||||
}
|
||||
template <int N, typename... U> static int _parse(_int<N>, PyObject *args, arg_tuple_t &tu, U... u) {
|
||||
return _parse(_int<N - 1>(), args, tu,
|
||||
converter_for_parser<typename std::tuple_element<N, typename std::decay<arg_tuple_t>::type>::type>,
|
||||
&std::get<N>(tu), u...);
|
||||
}
|
||||
|
||||
// the call function object ...
|
||||
// TODO : ADD THE REF AND POINTERS in x ??
|
||||
static PyObject *std_function_call(PyObject *self, PyObject *args, PyObject *kwds) {
|
||||
arg_tuple_t x;
|
||||
if (!_parse(_int_max(), args, x)) return NULL;
|
||||
try {
|
||||
return _call_and_treat_return(nop<R>(), (std_function *)self, x, make_index_seq<sizeof...(T)>());
|
||||
}
|
||||
CATCH_AND_RETURN("calling C++ std::function ", NULL);
|
||||
}
|
||||
|
||||
static PyTypeObject get_type() { return {
|
||||
PyObject_HEAD_INIT(NULL)
|
||||
0, /*ob_size*/
|
||||
"std_function", /*tp_name*/
|
||||
sizeof(std_function), /*tp_basicsize*/
|
||||
0, /*tp_itemsize*/
|
||||
(destructor)std_function_dealloc, /*tp_dealloc*/
|
||||
0, /*tp_print*/
|
||||
0, /*tp_getattr*/
|
||||
0, /*tp_setattr*/
|
||||
0, /*tp_compare*/
|
||||
0, /*tp_repr*/
|
||||
0, /*tp_as_number*/
|
||||
0, /*tp_as_sequence*/
|
||||
0, /*tp_as_mapping*/
|
||||
0, /*tp_hash */
|
||||
std_function_call, /*tp_call*/
|
||||
0, /*tp_str*/
|
||||
0, /*tp_getattro*/
|
||||
0, /*tp_setattro*/
|
||||
0, /*tp_as_buffer*/
|
||||
Py_TPFLAGS_DEFAULT , /*tp_flags*/
|
||||
"Internal wrapper of std::function", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
std_function_new, /* tp_new */
|
||||
};}
|
||||
|
||||
static void ensure_type_ready(PyTypeObject &Type, bool &ready) {
|
||||
if (!ready) {
|
||||
Type = get_type();
|
||||
if (PyType_Ready(&Type) < 0) std::cout << " aie ie " << std::endl;
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
// U can be anything, typically a lambda
|
||||
template <typename U> static PyObject *c2py(U &&x) {
|
||||
std_function *self;
|
||||
static PyTypeObject Type;
|
||||
static bool ready = false;
|
||||
ensure_type_ready(Type, ready);
|
||||
self = (std_function *)Type.tp_alloc(&Type, 0);
|
||||
if (self != NULL) {
|
||||
self->_c = new std::function<R(T...)>{ std::forward<U>(x) };
|
||||
}
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
static bool is_convertible(PyObject *ob, bool raise_exception) {
|
||||
if (PyCallable_Check(ob)) return true;
|
||||
if (raise_exception) { PyErr_SetString(PyExc_TypeError, "Can not convert to std::function a non callable object");}
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::function<R(T...)> py2c(PyObject * ob) {
|
||||
static PyTypeObject Type;
|
||||
static bool ready = false;
|
||||
ensure_type_ready(Type, ready);
|
||||
// If we convert a wrapped std::function, just extract it.
|
||||
if (PyObject_TypeCheck(ob, &Type)) { return *(((std_function *)ob)->_c);}
|
||||
// otherwise, we build a new std::function around the python function
|
||||
pyref py_fnt = borrowed(ob);
|
||||
auto l = [py_fnt](T... x) mutable -> R { // py_fnt is a pyref, it will keep the ref and manage the ref counting...
|
||||
pyref ret = PyObject_CallFunctionObjArgs(py_fnt, (PyObject*)pyref(convert_to_python(x))...,NULL);
|
||||
return py_converter<R>::py2c(ret);
|
||||
};
|
||||
return l;
|
||||
}
|
||||
};
|
||||
}}
|
Loading…
Reference in New Issue
Block a user