CEP 509 - Method templates

This is about "template methods": Methods that have a set of compile-time parameters (types and constants) and are duplicated once for each used combination of parameters.

The way this will used is to at compile-time create different "views" of an object, each view having different versions of methods. The underlying object is not "templates" or "parametrized" at all though. This is useful for integrating with libraries.

For creating new, template-based libraries within Cython, type templates (not described here) are a better solution. They are much less complicated to grasp, as they create a new run-time type for each combination of type argument. However, sometime one does not want to create a real new type, only simulate it in order to generate different code at compile-time.

The syntax below depends on function overloading, however alternative syntax could more explicitly use only the type arguments so that this requirement isn't in any way absolute.

Details

More syntax notes

The argument against @cython.template is that the method defined beneath it uses symbols that has significance as template parameters to the parser, and decorators thus might seem unnatural. Therefore a new keyword (template?) might be in order instead.

Guido once proposed using brackets for template arguments in a blog...something like this:

def foo(T a) [T]: ...

Python-like duck typing syntax

An alternative would also be to have

cdef foo(a, b, c): ...

automatically (or with a decorator or similar) do the same as (using Guido's template syntax):

cdef foo(A a, B b, C c) [A, B, C]: ...

which would basically allow for "compile-time duck-typing". While one might want to leave this for when template support is in place and stable, it could definitely be picked up again if Cython starts development for type inference etc.

typeof(a) could be used to reference the implicit A within the function.

One way of doing this could be to change the default rule of no type means object to no type means templated argument type. One could still specify object as the type if a template isn't wanted. This would make for a compelling syntax, but will break backwards compatability.

Example

Because this is only template methods and not a template class things might look a bit unusual, but for some situations they are still just right.

NumPy is one such example. The following schetches two operators and how to use them:

c_numpy.pxd:

cdef typedef struct numpy.ndarray ....:
    typeargs:
        type dtype
        int nd
        ...

    @cython.template("dtype", "nd")
    cdef dtype __getitem__(numpy.ndarray(dtype, nd) self, object indices):
        # code for accessing item
        # note that return type is specified

    # hypothetical Cython-specific operator to get the idea across
    @cython.template("dtype", "nd")
    def __coercefromobject__(numpy.ndarray(dtype, nd) self, x):
        if not isinstance(x, numpy.ndarray): raise TypeError(...)
        if x.dtype != dtype: raise ValueError(...)
        if x.dtype 
        return x

Usage:

a = numpy.zeros([10, 10], dtype=numpy.uint64)
# print a[4, 4] would give compile-time error, no compile-time args for __getitem__

c_numpy.ndarray(c_numpy.uint64, 2) b = a
print b[4, 5]

c_numpy.ndarray(c_numpy.uint64, 4) c = a

This would instantiate one version of __getitem__ and two versions of __coercefromobject__.

Function templates

One could also support functions:

@cython.template("T")
cdef T doubler(T value): return value * 2

cdef float f = doubler(4.5)
cdef int x = doubler(8)

Here, two versions of the doubler functions will be present in the C file, one for int and one for float.

enhancements/methodtemplates (last edited 2009-05-05 08:10:52 by DagSverreSeljebotn)