提交 a9acfba5 authored 作者: Arnaud Bergeron's avatar Arnaud Bergeron

Rework COp to allow all methods to be defined.

上级 0056cb74
...@@ -692,10 +692,9 @@ C ops by doing the following : ...@@ -692,10 +692,9 @@ C ops by doing the following :
C code file. This makes it easier to keep your Python and C code C code file. This makes it easier to keep your Python and C code
readable and well indented. readable and well indented.
* It automatically handles the methods :meth:`Op.c_code()`, * It can automatically handle all the methods that return C code,
:meth:`Op.c_support_code()`, :meth:`Op.c_support_code_apply()` and in addition to :meth:`Op.c_code_cache_version()` based on the
:meth:`Op.c_code_cache_version()` based on the provided external C provided external C implementation.
implementation.
To illustrate how much simpler the class ``COp`` makes the process of defining To illustrate how much simpler the class ``COp`` makes the process of defining
a new op with a C implementation, let's revisit the second example of this a new op with a C implementation, let's revisit the second example of this
...@@ -740,7 +739,7 @@ C file named vectorTimesVector.c : ...@@ -740,7 +739,7 @@ C file named vectorTimesVector.c :
.. code-block:: c .. code-block:: c
THEANO_SUPPORT_CODE_SECTION #section support_code
// Support code function // Support code function
bool vector_same_shape(PyArrayObject* arr1, PyArrayObject* arr2) bool vector_same_shape(PyArrayObject* arr1, PyArrayObject* arr2)
...@@ -749,7 +748,7 @@ C file named vectorTimesVector.c : ...@@ -749,7 +748,7 @@ C file named vectorTimesVector.c :
} }
THEANO_APPLY_CODE_SECTION #section support_code_apply
// Apply-specific support function // Apply-specific support function
void APPLY_SPECIFIC(vector_elemwise_mult)( void APPLY_SPECIFIC(vector_elemwise_mult)(
...@@ -822,45 +821,44 @@ this new version of the VectorTimesVector op : ...@@ -822,45 +821,44 @@ this new version of the VectorTimesVector op :
* Parent class : instead of inheriting from the class :class:`Op`, * Parent class : instead of inheriting from the class :class:`Op`,
VectorTimesVector inherits from the class ``COp``. VectorTimesVector inherits from the class ``COp``.
* Constructor : in our new op, the ``__init__()`` method has an important * Constructor : in our new op, the ``__init__()`` method has an
use; to inform the constructor of the ``COp`` class of the location, important use; to inform the constructor of the ``COp`` class
on the filesystem of the C implementation of this op. To do this, it of the location, on the filesystem of the C implementation of
gives the path of file containing the C code as well as the name of this op. To do this, it gives a list of file paths containing
the function, in that file, that should be called to perform the the C code for this op. To auto-generate the c_code method
computation. The path should be given as a relative path from the with a function call you can specify the function name as the
folder where the descendant of the ``COp`` class is defined. second parameter. The paths should be given as a relative
path from the folder where the descendant of the ``COp`` class
* ``make_node()`` : the ``make_node()`` method is absolutely identical to is defined.
the one in our old example. Using the ``COp`` class doesn't change
anything here. * ``make_node()`` : the ``make_node()`` method is absolutely
identical to the one in our old example. Using the ``COp``
* External C code : the external C code performs the computation class doesn't change anything here.
associated with the op. It contains, at the very least, a 'main' function
having the same name as provided to the constructor of the Python class * External C code : the external C code implements the various
``COp``. Writing this C code involves a few subtleties which deserve their functions associated with the op. associated with the op.
own respective sections. Writing this C code involves a few subtleties which deserve
their own respective sections.
Main function Main function
------------- -------------
The external C implementation must implement a main function whose name If you pass a function name to the ``__init__()`` method of the ``COp`` class, it must respect the following constraints:
is passed by the op to the ``__init__()`` method of the ``COp`` class. This
main C function must respect the following constraints :
* It must return an int. The value of that int indicates whether the * It must return an int. The value of that int indicates whether
op could perform its task or not. A value of 0 indicates success while the op could perform its task or not. A value of 0 indicates
any non-zero value will interrupt the execution of the Theano function. success while any non-zero value will interrupt the execution
Before returning a non-zero integer, the main function should call the of the Theano function. When returning non-zero the function
function ``PyErr_Format()`` to setup a Python exception. must set a python exception indicating the details of the
problem.
* It must receive one pointer for each input to the op followed by one * It must receive one argument for each input to the op followed by one
pointer to a pointer for each output of the op. pointer to an argument for each output of the op.
For example, the main C function of an op that takes two scalars as inputs and For example, the main C function of an op that takes two arrays as
returns both their sum and the difference between them would have four inputs and returns both their sum and the difference between them
parameters (two for the op's inputs and two for its outputs) and it's would have four parameters (two for the op's inputs and two for its
signature would look something like this : outputs) and it's signature would look something like this :
.. code-block:: c .. code-block:: c
...@@ -870,11 +868,21 @@ signature would look something like this : ...@@ -870,11 +868,21 @@ signature would look something like this :
Macros Macros
------ ------
The ``COp`` class defines a number of macros that can you can use in your C For certain section tags, your C code can benefit from a number of
implementation to make it simpler and more generic. pre-defined macros. These section tags have no macros: ``init_code``,
``c_support_code``. All other tags will have the support macros
discussed below.
For every input array 'i' (indexed from 0) of the op, the following macros are * ``APPLY_SPECIFIC(str)`` which will automatically append a name
defined: unique to the :ref:`Apply node that applies the Op at the end
of the provided ``str``. The use of this macro is discussed
futher below.
For every input which has a :attr:`dtype` attribute (this means
Tensors, and equivalent types on GPU), the following macros will be
defined unless your Op class has an :attr:`Op.check_input` attribute
defined to False. In these descrptions 'i' refers to the position
(indexed from 0) in the input array.
* ``DTYPE_INPUT_{i}`` : NumPy dtype of the data in the array. * ``DTYPE_INPUT_{i}`` : NumPy dtype of the data in the array.
This is the variable type corresponding to the NumPy dtype, not the This is the variable type corresponding to the NumPy dtype, not the
...@@ -889,71 +897,87 @@ defined: ...@@ -889,71 +897,87 @@ defined:
* ``TYPENUM_INPUT_{i}`` : Typenum of the data in the array * ``TYPENUM_INPUT_{i}`` : Typenum of the data in the array
* ``ITEMSIZE_INPUT_{i}`` : Size, in bytes, of the elements in the array. * ``ITEMSIZE_INPUT_{i}`` : Size, in bytes, of the elements in
the array.
In the same way, the macros ``DTYPE_OUTPUT_{i}``,
``ITEMSIZE_OUTPUT_{i}`` and ``TYPENUM_OUTPUT_{i}`` are defined for
every output 'i' of the op.
In the same way, the macros ``DTYPE_OUTPUT_{i}``, ``ITEMSIZE_OUTPUT_{i}`` and In addition to these macros, the ``init_code_struct``, ``code``, and
``TYPENUM_OUTPUT_{i}`` are defined for every output 'i' of the op. ``code_cleanup`` also have the following macros:
* ``FAIL`` : Code to insert at error points. A python exception
should be set prior to this code. An invocation look like this:
.. code-block:: c
if (error) {
// Set python exception
FAIL
}
The ``COp`` class also defines the macro ``APPLY_SPECIFIC(str)`` which will You can add a semicolon after the macro if it makes your editor
automatically append the name of the :ref:`Apply node that applies the Op at happy.
the end of the provided ``str``. The use of this macro is discussed below.
You should be aware, however, that these macros are apply-specific. As such, * ``CONTEXT`` : Name of the context variable for this node. (only
any function that uses them is considered to contain apply-specific code. for Ops which have a context, which is discussed elsewhere)
Finally the tag ``code`` and ``code_cleanup`` have macros to
pass the inputs and output names. These are name ``INPUT_{i}`` and
``OUTPUT_{i}`` where `i` is the 0-based index position in the input
and output arrays respectively.
Support code Support code
------------ ------------
The file whose name is provided to the ``COp`` class is not constrained to Certain section are limited in what you can place in them due to
contain only one function. It can in fact contain many functions, with every semantic and syntactic restrictions of the C++ language. Most of
function but the main one acting as support code. these restrictions apply to the tags that end in ``_struct``.
When we defined the VectorTimesVector op without using the ``COp`` class, we When we defined the VectorTimesVector op without using the ``COp``
had to make a distinction between two types of support_code : the support class, we had to make a distinction between two types of support_code
code that was apply-specific and the support code that wasn't. : the support code that was apply-specific and the support code that
The apply-specific code was defined in the ` c_support_code_apply()`` method wasn't. The apply-specific code was defined in the
and the elements defined in that code (global variables and functions) had to ``c_support_code_apply()`` method and the elements defined in that
include the name of the Apply node in their own names to avoid conflicts code (global variables and functions) had to include the name of the
between the different versions of the apply-specific code. The code that Apply node in their own names to avoid conflicts between the different
wasn't apply-specific was simply defined in the ``c_support_code()`` method. versions of the apply-specific code. The code that wasn't
apply-specific was simply defined in the ``c_support_code()`` method.
When using the ``COp`` class, we still have to make the distinction between
apply-specific and apply-agnostic support code but we express it differently To make indentifiers that include the :ref:`Apply` node name use the
in the code since it is all defined in the same external C file. ``APPLY_SPECIFIC(str)`` macro. In the above example, this macro is
These two types of support code should each be defined in their own section of used when defining the functions ``vector_elemwise_mult()`` and
the file, like in the example above. These sections should be delimited by the
markers ``THEANO_SUPPORT_CODE_SECTION`` (to be put on its own line, at the
beginning of the apply-agnostic support code section) and
``THEANO_APPLY_CODE_SECTION`` (to be put on its own line at the beginning of
the apply-specific code section). Moreover, just like in the previous examples
of this tutorial, apply-specific functions and global variables need to
include the name of the :ref:`Apply` node in their names. To achieve this,
the macro ``APPLY_SPECIFIC(str)`` should be used when defining those elements
as well as when referring to them. In the above example, this macro is used
when defining the functions ``vector_elemwise_mult()`` and
``vector_times_vector()`` as well as when calling function ``vector_times_vector()`` as well as when calling function
``vector_elemwise_mult()`` from inside ``vector_times_vector()``. ``vector_elemwise_mult()`` from inside ``vector_times_vector()``.
:note: When using the ``COp`` class, we still have to make the distinction
between C code for each of the methods of a C class. These sections of
The macro ``APPLY_SPECIFIC(str)`` should only ever be used for code are separated by ``#section <tag>`` markers. The tag determines
apply-specific code. It should not be used for apply-agnostic code. the name of the method this C code applies to with the rule that
``<tag>`` applies to `c_<tag>`. Unknown tags are an error and will be
The rules for knowing if a piece of code should be put in the apply-agnostic reported. Duplicate tags will be merged together in the order the
or the apply-specific support code section of the file are simple. If it uses appear in the C files.
any of the macros defined by the class ``COp`` then it is apply-specific and
goes in the corresponding section. If it calls any apply-specific code then The rules for knowing if where a piece of code should be put can be
it is apply-specific. Otherwise, it is apply-agnostic and goes in the sometimes tricky. The key thing to remember is that things that can
apply-agnostic support code section. be shared between instances of the op should be apply-agnostic and go
into a section which does not end in ``_apply`` or ``_struct``. The
In the above example, the ``function vector_same_shape()`` is apply-agnostic distinction of ``_apply`` and ``_struct`` mostly hinghes on how you
because it uses none of the macros defined by the class ``COp`` and it doesn't want to manange the lifetime of the object. Note that to use an
rely on any apply-specific code. The function ``vector_elemwise_mult()`` is apply-specific object, you have to be in a apply-specific section, so
apply-specific because it uses the macros defined by ``COp``. Finally, the some portions of the code that might seem apply-agnostic may still be
function ``vector_times_vector()`` is apply-specific because it uses those apply-specific because of the data they use (this does not include
same macros and also because it calls ``vector_elemwise_mult()`` which is an arguments).
apply-specific function.
In the above example, the ``function vector_same_shape()`` is
apply-agnostic because it uses none of the macros defined by the class
``COp`` and it doesn't rely on any apply-specific code. The function
``vector_elemwise_mult()`` is apply-specific because it uses the
macros defined by ``COp``. Finally, the function
``vector_times_vector()`` is apply-specific because it uses those same
macros and also because it calls ``vector_elemwise_mult()`` which is
an apply-specific function.
Final Note Final Note
========== ==========
......
...@@ -17,6 +17,7 @@ import logging ...@@ -17,6 +17,7 @@ import logging
import numpy import numpy
import os import os
import sys import sys
import re
import warnings import warnings
import theano import theano
...@@ -973,6 +974,32 @@ int main( int argc, const char* argv[] ) ...@@ -973,6 +974,32 @@ int main( int argc, const char* argv[] )
compute_map, no_recycling) compute_map, no_recycling)
def simple_meth(tag):
def f(self):
if tag in self.code_sections:
return self.code_sections[tag]
else:
raise utils.MethodNotDefined(
'c_' + tag, type(self), type(self).__name__)
f.__name__ = 'c_' + tag
return f
def apply_meth(tag):
def f(self, node, name):
if tag in self.code_sections:
code = self.code_sections[tag]
define_macros, undef_macros = self.get_c_macros(node, name)
return os.linesep.join([define_macros, code,
undef_macros])
else:
raise utils.MethodNotDefined(
'c_' + tag, type(self), type(self).__name__)
f.__name__ = 'c_' + tag
return f
class COp(Op): class COp(Op):
""" Class to allow an op to have an external C implementation. """ Class to allow an op to have an external C implementation.
...@@ -981,118 +1008,85 @@ class COp(Op): ...@@ -981,118 +1008,85 @@ class COp(Op):
the C implementation and the name of the function, in that file, to call the C implementation and the name of the function, in that file, to call
to perform the computations for the op. to perform the computations for the op.
""" """
section_re = re.compile(r'^#section ([a-zA-Z0-9_]+)$', re.MULTILINE)
# This is the set of allowed markers
SECTIONS = set([
'init_code', 'init_code_apply', 'init_code_struct',
'support_code', 'support_code_apply', 'support_code_struct',
'cleanup_code_struct',
'code', 'code_cleanup'])
def __init__(self, func_file, func_name): @classmethod
def get_path(cls, f):
self.func_file = func_file """
self.func_name = func_name Convert a path relative to the location of the class file into
an aboslute path. Paths that are already absolute are passed
# Define the markers that can be used to delimit sections in the through unchanged.
# external C code """
self.support_code_marker = "THEANO_SUPPORT_CODE_SECTION" if not os.path.isabs(f):
self.apply_code_marker = "THEANO_APPLY_CODE_SECTION" class_file = inspect.getfile(cls)
self.c_code_markers = [self.support_code_marker, class_dir = os.path.dirname(class_file)
self.apply_code_marker] f = os.path.realpath(os.path.join(class_dir, f))
return f
# Load the external C code def __init__(self, func_files, func_name=None):
try: """
# Attempt to find the file self.func_file in the folder where the Sections are loaded from files in order with sections in later
# concrete type of the COp instance is defined files overriding sections in previous files.
"""
# Get the name of the folder where the concrete type of the COp is if not isinstance(func_files, list):
# defined func_files = [func_files]
path_concrete_type = inspect.getfile(self.__class__)
folder_concrete_type = os.path.dirname(path_concrete_type)
# Try to open the file from there
f = open(os.path.join(folder_concrete_type, self.func_file), "r")
self.func_code = f.read()
f.close()
except IOError:
# Add information to the exception message to inform the user
# on the locations in which the class COp will look for the
# specified file
message = ("The path to the external C implementation should "
"be given as a relative path from the folder "
"where the Op is defined. ")
# Can't update the exception's message by modifying e.args
# because IOErrors don't use their attribute args to generate
# their error message
e.strerror = message + e.strerror
raise e
# Separate the contents of the file in sections and validate that at
# lest one of the necessary code sections has been defined
self.code_sections = self.parse_external_c_code(self.func_code)
if sum([marker in self.code_sections.keys()
for marker in self.c_code_markers]) == 0:
raise(RuntimeError, "The provided C implementation does not "
"define a support code section or a support code apply "
"section.")
def parse_external_c_code(self, code):
# Obtain the positions of the C code markers used in the C code
positions = [(code.index(marker), marker)
for marker in self.c_code_markers if marker in code]
# Go over the markers in their order of occurence and extract
# the C code they concern
positions.sort()
code_sections = {}
for i in range(len(positions)):
marker_start, marker = positions[i]
if i < len(positions) - 1:
# This is not the last section in the code : extract the code
# between the beginning of the current marker and the
# beginning of the next one.
next_marker_start = positions[i+1][0]
section = code[marker_start: next_marker_start]
else:
# This is the last section in the code : extract the remaining
# C code
section = code[marker_start:]
cleaned_section = section.replace(marker, "") self.func_files = [self.get_path(f) for f in func_files]
code_sections[marker] = cleaned_section self.func_name = func_name
return code_sections self.load_c_code()
if len(self.code_sections) == 0:
raise ValueError("No sections where defined in C files")
if self.func_name is not None:
if 'op_code' in self.code_sections:
# maybe a warning instead (and clearing the key)
raise ValueError('Cannot have an "op_code" section and '
'specify the func_name')
if 'op_code_cleanup' in self.code_sections:
# maybe a warning instead (and clearing the key)
raise ValueError('Cannot have an "op_code_cleanup" section '
'and specify the func_name')
def load_c_code(self):
self.func_codes = []
for func_file in self.func_files:
with open(func_file, 'r') as f:
self.func_codes.append(f.read())
self.code_sections = dict()
for i, code in enumerate(self.func_codes):
split = self.section_re.split(code)
if split[0].strip() != '':
raise ValueError('Stray code before first #section '
'statement (in file %s): %s' %
(self.func_files[i], split[0]))
n = 1
while n < len(split):
if split[n] not in self.SECTIONS:
raise ValueError("Unknown section type (in file %s): %s" %
(self.fun_files[i], split[n]))
if split[n] not in self.code_sections:
self.code_sections[split[n]] = ""
self.code_sections[split[n]] += split[n+1]
n += 2
def c_code_cache_version(self): def c_code_cache_version(self):
return hash(self.func_code) return hash(tuple(self.func_codes))
def c_support_code(self):
if self.support_code_marker in self.code_sections:
return self.code_sections[self.support_code_marker]
else:
raise utils.MethodNotDefined("c_support_code",
type(self), self.__class__.__name__)
def c_support_code_apply(self, node, name):
if self.apply_code_marker in self.code_sections:
apply_code = self.code_sections[self.apply_code_marker]
if hasattr(self, 'check_inputs') and self.check_inputs == False:
return apply_code
else:
define_macros, undef_macros = self.get_c_macros(node, name)
return os.linesep.join([define_macros, apply_code,
undef_macros])
else:
raise utils.MethodNotDefined("c_support_code_apply",
type(self), self.__class__.__name__)
c_init_code = simple_meth('init_code')
c_init_code_apply = apply_meth('init_code_apply')
c_support_code = simple_meth('support_code')
c_support_code_apply = apply_meth('support_code_apply')
c_support_code_struct = apply_meth('support_code_struct')
c_cleanup_code_struct = apply_meth('cleanup_code_struct')
def format_c_function_args(self, inp, out): def format_c_function_args(self, inp, out):
# Generate an string containing the arguments sent to the external C # Generate an string containing the arguments sent to the external C
...@@ -1100,73 +1094,149 @@ class COp(Op): ...@@ -1100,73 +1094,149 @@ class COp(Op):
# "input0, input1, input2, &output0, &output1" # "input0, input1, input2, &output0, &output1"
return ", ".join(list(inp) + ["&%s" % o for o in out]) return ", ".join(list(inp) + ["&%s" % o for o in out])
def get_c_macros(self, node, name): def get_c_macros(self, node, name, check_input=None):
define_template = "#define %s %s"
undef_template = "#undef %s"
define_macros = []
undef_macros = []
define_template = "#define %s %s" + os.linesep if check_input is None:
undef_template = "#undef %s" + os.linesep check_input = getattr(self, 'check_input', True)
define_macros = ""
undef_macros = ""
if check_input:
# Extract the various properties of the input and output variables # Extract the various properties of the input and output variables
variables = node.inputs + node.outputs variables = node.inputs + node.outputs
variable_names = (["INPUT_%i" % i for i in range(len(node.inputs))] + variable_names = (["INPUT_%i" % i for i in range(len(node.inputs))] +
["OUTPUT_%i" % i for i in range(len(node.inputs))]) ["OUTPUT_%i" % i for i in range(len(node.inputs))])
variable_dtypes_names = [v.dtype for v in variables]
variable_dtypes = [numpy.dtype(d) for d in variable_dtypes_names]
variable_typenums = [d.num for d in variable_dtypes]
variable_itemsizes = [d.itemsize for d in variable_dtypes]
# Generate dtype macros # Generate dtype macros
for i in range(len(variables)): for i, v in enumerate(variables):
macro_name = "DTYPE_" + variable_names[i] if not hasattr(v, 'dtype'):
macro_value = "npy_" + variable_dtypes_names[i] continue
vname = variable_names[i]
macro_name = "DTYPE_" + vname
macro_value = "npy_" + v.dtype
define_macros += define_template % (macro_name, macro_value) define_macros.append(define_template % (macro_name, macro_value))
undef_macros += undef_template % macro_name undef_macros.append(undef_template % macro_name)
# Generate typenum macros d = numpy.dtype(v.dtype)
for i in range(len(variables)):
macro_name = "TYPENUM_" + variable_names[i]
macro_value = variable_typenums[i]
define_macros += define_template % (macro_name, macro_value) macro_name = "TYPENUM_" + vname
undef_macros += undef_template % macro_name macro_value = d.num
# Generate itemsize macros define_macros.append(define_template % (macro_name, macro_value))
for i in range(len(variables)): undef_macros.append(undef_template % macro_name)
macro_name = "ITEMSIZE_" + variable_names[i]
macro_value = variable_itemsizes[i]
define_macros += define_template % (macro_name, macro_value) macro_name = "ITEMSIZE_" + vname
undef_macros += undef_template % macro_name macro_value = d.itemsize
define_macros.append(define_template % (macro_name, macro_value))
undef_macros.append(undef_template % macro_name)
# Generate a macro to mark code as being apply-specific # Generate a macro to mark code as being apply-specific
define_macros += define_template % ("APPLY_SPECIFIC(str)", define_macros.append(define_template % ("APPLY_SPECIFIC(str)",
"str##_%s" % name) "str##_%s" % name))
undef_macros += undef_template % "APPLY_SPECIFIC" undef_macros.append(undef_template % "APPLY_SPECIFIC")
return os.linesep.join(define_macros), os.linesep.join(undef_macros)
def _lquote_macro(self, txt):
res = []
spl = txt.split('\n')
for l in spl[:-1]:
res.append(l + ' \\')
res.append(spl[-1])
return os.linesep.join(res)
def get_sub_macros(self, sub):
define_macros = []
undef_macros = []
define_macros.append("#define FAIL %s" %
(self._lquote_macro(sub['fail']),))
undef_macros.append("#undef FAIL")
if 'context' in sub:
define_macros.append("#define CONTEXT %s" % (sub['context'],))
undef_macos.append("#undef CONTEXT")
return os.linesep.join(define_macros), os.linesep.join(undef_macros)
def get_io_macros(self, inputs, outputs):
define_macros = []
undef_macros = []
for i, inp in enumerate(inputs):
define_macros.append("#define INPUT_%d %s" (i, inp))
undef_macros.append("#undef INPUT_%d", (i,))
for i, out in enumerate(outputs):
define_macros.append("#define OUTPUT_%d %s" (i, inp))
undef_macros.append("#undef OUTPUT_%d", (i,))
return define_macros, undef_macros def c_init_code_struct(self, node, name, sub):
if 'init_code_struct' in self.code_sections:
op_code = self.code_sections['init_code_struct']
def c_code(self, node, name, inp, out, sub): def_macros, undef_macros = self.get_c_macros(node, name)
def_sub, undef_sub = self.get_sub_macros(sub)
return os.linesep.join([def_macros, def_sub,
op_code,
undef_sub, undef_macros])
else:
raise utils.MethodNotDefined(
'c_init_code_struct', type(self), type(self).__name__)
def c_code(self, node, name, inp, out, sub):
if self.func_name is not None:
assert 'code' not in self.code_sections
func_name = self.func_name func_name = self.func_name
func_args = self.format_c_function_args(inp, out) func_args = self.format_c_function_args(inp, out)
fail = sub['fail'] fail = sub['fail']
# Generate the code to define/undefine the C macros define_macros, undef_macros = self.get_c_macros(node, name,
define_macros, undef_macros = self.get_c_macros(node, name) check_input=False)
# Generate the C code # Generate the C code
c_code = """ return """
%(define_macros)s %(define_macros)s
{ {
int result = %(func_name)s(%(func_args)s); if (%(func_name)s(%(func_args)s) != 0) {
if (result != 0) %(fail)s
{
%(fail)s;
}
} }
%(undef_macros)s }
""" % locals() %(undef_macros)s
""" % dict(func_name=self.func_name, fail=sub['fail'],
func_args=self.format_c_function_args(inp, out),
define_macros=define_macros, undef_macros=undef_macros)
else:
if 'code' in self.code_sections:
op_code = self.code_sections['code']
return c_code def_macros, undef_macros = self.get_c_macros(node, name)
def_sub, undef_sub = self.get_sub_macros(sub)
def_io, undef_io = self.get_io_macros(inp, out)
return os.linesep.join([def_macros, def_sub, def_io,
op_code,
undef_io, undef_sub, undef_macros])
else:
raise utils.MethodNotDefined(
'c_code', type(self), type(self).__name__)
def c_code_cleanup(self, node, name, inputs, outputs, sub):
if 'code_cleanup' in self.code_sections:
op_code = self.code_sections['code_cleanup']
def_macros, undef_macros = self.get_c_macros(node, name)
def_sub, undef_sub = self.get_sub_macros(sub)
def_io, undef_io = self.get_io_macros(inp, out)
return os.linesep.join([def_macros, def_sub, def_io,
op_code,
undef_io, undef_sub, undef_macros])
else:
raise utils.MethodNotDefined(
'c_code_cleanup', type(self), type(self).__name__)
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论