diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 8fa41a3b314ff59294b6d9c991f799a77fe6e6e1..c20484621e0f5947fb9e18128096fc03e80180ea 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -16,7 +16,7 @@ from .Code import UtilityCode, LazyUtilityCode, TempitaUtilityCode from . import StringEncoding from . import Naming -from .Errors import error +from .Errors import error, warning class BaseType(object): @@ -3555,12 +3555,20 @@ class CppClassType(CType): error(pos, "'%s' type is not a template" % self) return error_type if len(self.templates) - self.num_optional_templates <= len(template_values) < len(self.templates): + num_defaults = len(self.templates) - len(template_values) partial_specialization = self.declaration_code('', template_params=template_values) + # Most of the time we don't need to declare anything typed to these + # default template arguments, but when we do there's no way in C++ + # to reference this directly. However, it is common convention to + # provide a typedef in the template class that resolves to each + # template type. For now, allow the user to specify this name as + # the template parameter. + # TODO: Allow typedefs in cpp classes and search for it in this + # classes scope as a concrete name we could use. template_values = template_values + [ - TemplatePlaceholderType("%s %s::%s" % ( - TemplatePlaceholderType.UNDECLARABLE_DEFAULT, partial_specialization, param.name), - True) - for param in self.templates[-self.num_optional_templates:]] + TemplatePlaceholderType( + "%s::%s" % (partial_specialization, param.name), True) + for param in self.templates[-num_defaults:]] if len(self.templates) != len(template_values): error(pos, "%s templated type receives %d arguments, got %d" % (self.name, len(self.templates), len(template_values))) @@ -3684,17 +3692,12 @@ class CppClassType(CType): class TemplatePlaceholderType(CType): - UNDECLARABLE_DEFAULT = "undeclarable default " - def __init__(self, name, optional=False): self.name = name self.optional = optional def declaration_code(self, entity_code, for_display = 0, dll_linkage = None, pyrex = 0): - if self.name.startswith(self.UNDECLARABLE_DEFAULT) and not for_display: - error(None, "Can't declare variable of type '%s'" - % self.name[len(self.UNDECLARABLE_DEFAULT) + 1:]) if entity_code: return self.name + " " + entity_code else: diff --git a/docs/src/userguide/wrapping_CPlusPlus.rst b/docs/src/userguide/wrapping_CPlusPlus.rst index bec84c761e0fafeca57381e4201b5159936af7b3..92791f3454db321f4e2e18a4efa71ca4484fb19b 100644 --- a/docs/src/userguide/wrapping_CPlusPlus.rst +++ b/docs/src/userguide/wrapping_CPlusPlus.rst @@ -68,7 +68,7 @@ document. Let's assume it will be in a header file called void move(int dx, int dy); }; } - + and the implementation in the file called :file:`Rectangle.cpp`: .. sourcecode:: c++ @@ -104,7 +104,7 @@ and the implementation in the file called :file:`Rectangle.cpp`: x1 += dx; y1 += dy; } - + } This is pretty dumb, but should suffice to demonstrate the steps involved. @@ -196,7 +196,7 @@ class name from Rectangle.h and adjust for Cython syntax, so now it becomes:: cdef extern from "Rectangle.h" namespace "shapes": cdef cppclass Rectangle: - + Add public attributes ^^^^^^^^^^^^^^^^^^^^^^ @@ -346,9 +346,9 @@ nested in Cython:: T& at(int) iterator begin() iterator end() - + cdef vector[int].iterator iter #iter is declared as being of type vector<int>::iterator - + Note that the nested class is declared with a ``cppclass`` but without a ``cdef``. C++ operators not compatible with Python syntax @@ -409,8 +409,15 @@ Cython uses a bracket syntax for templating. A simple example for wrapping C++ v del v -Multiple template parameters can be defined as a list, such as [T, U, V] -or [int, bool, char]. Template functions are defined similarly, with +Multiple template parameters can be defined as a list, such as ``[T, U, V]`` +or ``[int, bool, char]``. Optional template parameters can be indicated +by writing ``[T, U, V=*]``. In the event that Cython needs to explicitly +reference a default template value for an incomplete template instantiation, +it will write ``MyClass<T, U>::V``, so if the class provides a typedef +for its template parameters it is preferable to use that name here. + + +Template functions are defined similarly to class templates, with the template parameter list following the function name:: cdef extern from "<algorithm>" namespace "std": @@ -437,7 +444,7 @@ For example:: vect.push_back(i) for i in range(10): print vect[i] - + The pxd files in ``/Cython/Includes/libcpp`` also work as good examples on how to declare C++ classes. @@ -601,7 +608,7 @@ module which: * includes the needed C headers in an extern "C" block * contains minimal forwarding functions in C++, each of which calls the - respective pure-C function + respective pure-C function Declaring/Using References --------------------------- @@ -614,5 +621,3 @@ C++ left-values C++ allows functions returning a reference to be left-values. This is currently not supported in Cython. ``cython.operator.dereference(foo)`` is also not considered a left-value. - - diff --git a/tests/run/cpp_templates.pyx b/tests/run/cpp_templates.pyx index b68f2f5218a77751a25786c84c5024510de83e18..88365c86142b1ce13a7d6c82b9c597d3917c9f7d 100644 --- a/tests/run/cpp_templates.pyx +++ b/tests/run/cpp_templates.pyx @@ -3,14 +3,17 @@ from cython.operator import dereference as deref cdef extern from "cpp_templates_helper.h": - cdef cppclass Wrap[T, S=*]: + cdef cppclass Wrap[T, AltType=*, UndeclarableAltType=*]: Wrap(T) void set(T) T get() bint operator==(Wrap[T]) - S get_alt_type() - void set_alt_type(S) + AltType get_alt_type() + void set_alt_type(AltType) + + UndeclarableAltType create() + bint accept(UndeclarableAltType) cdef cppclass Pair[T1,T2]: Pair(T1,T2) @@ -68,15 +71,17 @@ def test_default_template_arguments(double x): """ try: a = new Wrap[double](x) - b = new Wrap[double, int](x) + b = new Wrap[double, int, long](x) + + ax = a.get_alt_type() + a.set_alt_type(ax) + assert a.accept(a.create()) # never declared -# ax = a.get_alt_type() -# a.set_alt_type(ax) - a.set_alt_type(a.get_alt_type()) + bx = b.get_alt_type() + b.set_alt_type(bx) -# bx = b.get_alt_type() -# b.set_alt_type(bx) - b.set_alt_type(b.get_alt_type()) + bc = b.create() # declaration here is fine + assert b.accept(bc) return a.get(), b.get() finally: diff --git a/tests/run/cpp_templates_helper.h b/tests/run/cpp_templates_helper.h index a685db6afcf36a53f776b25826cbc01e81e1f670..8973e6c5bf618d00af4428ae5ec9c2df61801083 100644 --- a/tests/run/cpp_templates_helper.h +++ b/tests/run/cpp_templates_helper.h @@ -1,7 +1,9 @@ -template <typename T, typename S=T> +template <typename T, typename S=T, typename U=T> class Wrap { T value; public: + typedef S AltType; + Wrap(T v) : value(v) { } void set(T v) { value = v; } T get(void) { return value; } @@ -9,6 +11,9 @@ public: S get_alt_type(void) { return (S) value; } void set_alt_type(S v) { value = (T) v; } + + U create(void) { return (U) value; } + bool accept(U v) { return v == (U) value; } }; template <class T1, class T2>