Using C++ in Cython

Overview

Cython has native support for most of the C++ language. Specifically:

  • C++ objects can be dynamically allocated with new and del keywords.
  • C++ objects can be stack-allocated.
  • C++ classes can be declared with the new keyword cppclass.
  • Templated classes and functions are supported.
  • Overloaded functions are supported.
  • Overloading of C++ operators (such as operator+, operator[],…) is supported.

Procedure Overview

The general procedure for wrapping a C++ file can now be described as follows:

  • Specify C++ language in a setup.py script or locally in a source file.
  • Create one or more .pxd files with cdef extern from blocks and(if existing) the C++ namespace name. In these blocks:
    • declare classes as cdef cppclass blocks
    • declare public names (variables, methods and constructors)
  • cimport them in one or more extension modules (.pyx files).

A simple Tutorial

An example C++ API

Here is a tiny C++ API which we will use as an example throughout thisdocument. Let’s assume it will be in a header file calledRectangle.h:

  1. #ifndef RECTANGLE_H
  2. #define RECTANGLE_H
  3.  
  4. namespace shapes {
  5. class Rectangle {
  6. public:
  7. int x0, y0, x1, y1;
  8. Rectangle();
  9. Rectangle(int x0, int y0, int x1, int y1);
  10. ~Rectangle();
  11. int getArea();
  12. void getSize(int* width, int* height);
  13. void move(int dx, int dy);
  14. };
  15. }
  16.  
  17. #endif

and the implementation in the file called Rectangle.cpp:

  1. #include <iostream>
  2. #include "Rectangle.h"
  3.  
  4. namespace shapes {
  5.  
  6. // Default constructor
  7. Rectangle::Rectangle () {}
  8.  
  9. // Overloaded constructor
  10. Rectangle::Rectangle (int x0, int y0, int x1, int y1) {
  11. this->x0 = x0;
  12. this->y0 = y0;
  13. this->x1 = x1;
  14. this->y1 = y1;
  15. }
  16.  
  17. // Destructor
  18. Rectangle::~Rectangle () {}
  19.  
  20. // Return the area of the rectangle
  21. int Rectangle::getArea () {
  22. return (this->x1 - this->x0) * (this->y1 - this->y0);
  23. }
  24.  
  25. // Get the size of the rectangle.
  26. // Put the size in the pointer args
  27. void Rectangle::getSize (int *width, int *height) {
  28. (*width) = x1 - x0;
  29. (*height) = y1 - y0;
  30. }
  31.  
  32. // Move the rectangle by dx dy
  33. void Rectangle::move (int dx, int dy) {
  34. this->x0 += dx;
  35. this->y0 += dy;
  36. this->x1 += dx;
  37. this->y1 += dy;
  38. }
  39. }

This is pretty dumb, but should suffice to demonstrate the steps involved.

Declaring a C++ class interface

The procedure for wrapping a C++ class is quite similar to that for wrappingnormal C structs, with a couple of additions. Let’s start here by creating thebasic cdef extern from block:

  1. cdef extern from "Rectangle.h" namespace "shapes":

This will make the C++ class def for Rectangle available. Note the namespace declaration.Namespaces are simply used to make the fully qualified name of the object,and can be nested (e.g. "outer::inner") or even refer toclasses (e.g. "namespace::MyClass to declare static members on MyClass).

Declare class with cdef cppclass

Now, let’s add the Rectangle class to this extern from block - just copy theclass name from Rectangle.h and adjust for Cython syntax, so now it becomes:

  1. cdef extern from "Rectangle.h" namespace "shapes":
  2. cdef cppclass Rectangle:

Add public attributes

We now need to declare the attributes and methods for use on Cython. We put those declarationsin a file called Rectangle.pxd. You can see it as a header filewhich is readable by Cython:

  1. cdef extern from "Rectangle.cpp":
  2. pass
  3.  
  4. # Declare the class with cdef
  5. cdef extern from "Rectangle.h" namespace "shapes":
  6. cdef cppclass Rectangle:
  7. Rectangle() except +
  8. Rectangle(int, int, int, int) except +
  9. int x0, y0, x1, y1
  10. int getArea()
  11. void getSize(int* width, int* height)
  12. void move(int, int)

Note that the constructor is declared as “except +”. If the C++ code orthe initial memory allocation raises an exception due to a failure, thiswill let Cython safely raise an appropriate Python exception instead(see below). Without this declaration, C++ exceptions originating fromthe constructor will not be handled by Cython.

We use the lines:

  1. cdef extern from "Rectangle.cpp":
  2. pass

to include the C++ code from Rectangle.cpp. It is also possible to specify tosetuptools that Rectangle.cpp is a source. To do that, you can add this directive at thetop of the .pyx (not .pxd) file:

  1. # distutils: sources = Rectangle.cpp

Note that when you use cdef extern from, the path that you specify is relative to the currentfile, but if you use the distutils directive, the path is relative to thesetup.py. If you want to discover the path of the sources whenrunning the setup.py, you can use the aliases argumentof the cythonize() function.

Declare a var with the wrapped C++ class

We’ll create a .pyx file named rect.pyx to build our wrapper. We’reusing a name other than Rectangle, but if you prefer giving the same nameto the wrapper as the C++ class, see the section onresolving naming conflicts.

Within, we use cdef to declare a var of the class with the C++ new statement:

  1. # distutils: language = c++
  2.  
  3. from Rectangle cimport Rectangle
  4.  
  5. def main():
  6. rec_ptr = new Rectangle(1, 2, 3, 4) # Instantiate a Rectangle object on the heap
  7. try:
  8. rec_area = rec_ptr.getArea()
  9. finally:
  10. del rec_ptr # delete heap allocated object
  11.  
  12. cdef Rectangle rec_stack # Instantiate a Rectangle object on the stack

The line:

  1. # distutils: language = c++

is to indicate to Cython that this .pyx file has to be compiled to C++.

It’s also possible to declare a stack allocated object, as long as it hasa “default” constructor:

  1. cdef extern from "Foo.h":
  2. cdef cppclass Foo:
  3. Foo()
  4.  
  5. def func():
  6. cdef Foo foo
  7. ...

Note that, like C++, if the class has only one constructor and itis a nullary one, it’s not necessary to declare it.

Create Cython wrapper class

At this point, we have exposed into our pyx file’s namespace the interfaceof the C++ Rectangle type. Now, we need to make this accessible fromexternal Python code (which is our whole point).

Common programming practice is to create a Cython extension type whichholds a C++ instance as an attribute and create a bunch offorwarding methods. So we can implement the Python extension type as:

  1. # distutils: language = c++
  2.  
  3. from Rectangle cimport Rectangle
  4.  
  5. # Create a Cython extension type which holds a C++ instance
  6. # as an attribute and create a bunch of forwarding methods
  7. # Python extension type.
  8. cdef class PyRectangle:
  9. cdef Rectangle c_rect # Hold a C++ instance which we're wrapping
  10.  
  11. def __cinit__(self, int x0, int y0, int x1, int y1):
  12. self.c_rect = Rectangle(x0, y0, x1, y1)
  13.  
  14. def get_area(self):
  15. return self.c_rect.getArea()
  16.  
  17. def get_size(self):
  18. cdef int width, height
  19. self.c_rect.getSize(&width, &height)
  20. return width, height
  21.  
  22. def move(self, dx, dy):
  23. self.c_rect.move(dx, dy)

And there we have it. From a Python perspective, this extension type will lookand feel just like a natively defined Rectangle class.It should be noted that if you want to giveattribute access, you could just implement some properties:

  1. # distutils: language = c++
  2.  
  3. from Rectangle cimport Rectangle
  4.  
  5. cdef class PyRectangle:
  6. cdef Rectangle c_rect
  7.  
  8. def __cinit__(self, int x0, int y0, int x1, int y1):
  9. self.c_rect = Rectangle(x0, y0, x1, y1)
  10.  
  11. def get_area(self):
  12. return self.c_rect.getArea()
  13.  
  14. def get_size(self):
  15. cdef int width, height
  16. self.c_rect.getSize(&width, &height)
  17. return width, height
  18.  
  19. def move(self, dx, dy):
  20. self.c_rect.move(dx, dy)
  21.  
  22. # Attribute access
  23. @property
  24. def x0(self):
  25. return self.c_rect.x0
  26. @x0.setter
  27. def x0(self, x0):
  28. self.c_rect.x0 = x0
  29.  
  30. # Attribute access
  31. @property
  32. def x1(self):
  33. return self.c_rect.x1
  34. @x1.setter
  35. def x1(self, x1):
  36. self.c_rect.x1 = x1
  37.  
  38. # Attribute access
  39. @property
  40. def y0(self):
  41. return self.c_rect.y0
  42. @y0.setter
  43. def y0(self, y0):
  44. self.c_rect.y0 = y0
  45.  
  46. # Attribute access
  47. @property
  48. def y1(self):
  49. return self.c_rect.y1
  50. @y1.setter
  51. def y1(self, y1):
  52. self.c_rect.y1 = y1

Cython initializes C++ class attributes of a cdef class using the nullary constructor.If the class you’re wrapping does not have a nullary constructor, you must store a pointerto the wrapped class and manually allocate and deallocate it.A convenient and safe place to do so is in the cinit and dealloc methodswhich are guaranteed to be called exactly once upon creation and deletion of the Pythoninstance.

  1. # distutils: language = c++
  2.  
  3. from Rectangle cimport Rectangle
  4.  
  5. cdef class PyRectangle:
  6. cdef Rectangle*c_rect # hold a pointer to the C++ instance which we're wrapping
  7.  
  8. def __cinit__(self, int x0, int y0, int x1, int y1):
  9. self.c_rect = new Rectangle(x0, y0, x1, y1)
  10.  
  11. def __dealloc__(self):
  12. del self.c_rect

Compilation and Importing

To compile a Cython module, it is necessary to have a setup.py file:

  1. from setuptools import setup
  2.  
  3. from Cython.Build import cythonize
  4.  
  5. setup(ext_modules=cythonize("rect.pyx"))

Run $ python setup.py build_ext —inplace

To test it, open the Python interpreter:

  1. >>> import rect
  2. >>> x0, y0, x1, y1 = 1, 2, 3, 4
  3. >>> rect_obj = rect.PyRectangle(x0, y0, x1, y1)
  4. >>> print(dir(rect_obj))
  5. ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
  6. '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__',
  7. '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
  8. '__setstate__', '__sizeof__', '__str__', '__subclasshook__', 'get_area', 'get_size', 'move']

Advanced C++ features

We describe here all the C++ features that were not discussed in the above tutorial.

Overloading

Overloading is very simple. Just declare the method with different parametersand use any of them:

  1. cdef extern from "Foo.h":
  2. cdef cppclass Foo:
  3. Foo(int)
  4. Foo(bool)
  5. Foo(int, bool)
  6. Foo(int, int)

Overloading operators

Cython uses C++ naming for overloading operators:

  1. cdef extern from "foo.h":
  2. cdef cppclass Foo:
  3. Foo()
  4. Foo operator+(Foo)
  5. Foo operator-(Foo)
  6. int operator*(Foo)
  7. int operator/(int)
  8. int operator*(int, Foo) # allows 1*Foo()
  9. # nonmember operators can also be specified outside the class
  10. double operator/(double, Foo)
  11.  
  12.  
  13. cdef Foo foo = new Foo()
  14.  
  15. foo2 = foo + foo
  16. foo2 = foo - foo
  17.  
  18. x = foo * foo2
  19. x = foo / 1
  20.  
  21. x = foo[0] * foo2
  22. x = foo[0] / 1
  23. x = 1*foo[0]
  24.  
  25. cdef double y
  26. y = 2.0/foo[0]

Note that if one has pointers to C++ objects, dereferencing must be doneto avoid doing pointer arithmetic rather than arithmetic on the objectsthemselves:

  1. cdef Foo* foo_ptr = new Foo()
  2. foo = foo_ptr[0] + foo_ptr[0]
  3. x = foo_ptr[0] / 2
  4.  
  5. del foo_ptr

Nested class declarations

C++ allows nested class declaration. Class declarations can also benested in Cython:

  1. # distutils: language = c++
  2.  
  3. cdef extern from "<vector>" namespace "std":
  4. cdef cppclass vector[T]:
  5. cppclass iterator:
  6. T operator*()
  7. iterator operator++()
  8. bint operator==(iterator)
  9. bint operator!=(iterator)
  10. vector()
  11. void push_back(T&)
  12. T& operator[](int)
  13. T& at(int)
  14. iterator begin()
  15. iterator end()
  16.  
  17. 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,as it is already part of a cdef declaration section.

C++ operators not compatible with Python syntax

Cython tries to keep its syntax as close as possible to standard Python.Because of this, certain C++ operators, like the preincrement ++fooor the dereferencing operator *foo cannot be used with the samesyntax as C++. Cython provides functions replacing these operators ina special module cython.operator. The functions provided are:

  • cython.operator.dereference for dereferencing. dereference(foo)will produce the C++ code *(foo)
  • cython.operator.preincrement for pre-incrementation. preincrement(foo)will produce the C++ code ++(foo).Similarly for predecrement, postincrement and postdecrement.
  • cython.operator.comma for the comma operator. comma(a, b)will produce the C++ code ((a), (b)).

These functions need to be cimported. Of course, one can use afrom … cimport … as to have shorter and more readable functions.For example: from cython.operator cimport dereference as deref.

For completeness, it’s also worth mentioning cython.operator.addresswhich can also be written &foo.

Templates

Cython uses a bracket syntax for templating. A simple example for wrapping C++ vector:

  1. # distutils: language = c++
  2.  
  3. # import dereference and increment operators
  4. from cython.operator cimport dereference as deref, preincrement as inc
  5.  
  6. cdef extern from "<vector>" namespace "std":
  7. cdef cppclass vector[T]:
  8. cppclass iterator:
  9. T operator*()
  10. iterator operator++()
  11. bint operator==(iterator)
  12. bint operator!=(iterator)
  13. vector()
  14. void push_back(T&)
  15. T& operator[](int)
  16. T& at(int)
  17. iterator begin()
  18. iterator end()
  19.  
  20. cdef vector[int] *v = new vector[int]()
  21. cdef int i
  22. for i in range(10):
  23. v.push_back(i)
  24.  
  25. cdef vector[int].iterator it = v.begin()
  26. while it != v.end():
  27. print(deref(it))
  28. inc(it)
  29.  
  30. del v

Multiple template parameters can be defined as a list, such as [T, U, V]or [int, bool, char]. Optional template parameters can be indicatedby writing [T, U, V=*]. In the event that Cython needs to explicitlyreference the type of a default template parameter for an incomplete templateinstantiation, it will write MyClass<T, U>::V, so if the class providesa typedef for its template parameters it is preferable to use that name here.

Template functions are defined similarly to class templates, withthe template parameter list following the function name:

  1. # distutils: language = c++
  2.  
  3. cdef extern from "<algorithm>" namespace "std":
  4. T max[T](T a, T b)
  5.  
  6. print(max[long](3, 4))
  7. print(max(1.5, 2.5)) # simple template argument deduction

Standard library

Most of the containers of the C++ Standard Library have been declaredin pxd files locatedin /Cython/Includes/libcpp.These containers are: deque, list, map, pair, queue, set, stack, vector.

For example:

  1. # distutils: language = c++
  2.  
  3. from libcpp.vector cimport vector
  4.  
  5. cdef vector[int] vect
  6. cdef int i, x
  7.  
  8. for i in range(10):
  9. vect.push_back(i)
  10.  
  11. for i in range(10):
  12. print(vect[i])
  13.  
  14. for x in vect:
  15. print(x)

The pxd filesin /Cython/Includes/libcppalso work as good examples on how to declare C++ classes.

The STL containers coerce from and to thecorresponding Python builtin types. The conversion is triggeredeither by an assignment to a typed variable (including typed functionarguments) or by an explicit cast, e.g.:

  1. # distutils: language = c++
  2.  
  3. from libcpp.string cimport string
  4. from libcpp.vector cimport vector
  5.  
  6. py_bytes_object = b'The knights who say ni'
  7. py_unicode_object = u'Those who hear them seldom live to tell the tale.'
  8.  
  9. cdef string s = py_bytes_object
  10. print(s) # b'The knights who say ni'
  11.  
  12. cdef string cpp_string = <string> py_unicode_object.encode('utf-8')
  13. print(cpp_string) # b'Those who hear them seldom live to tell the tale.'
  14.  
  15. cdef vector[int] vect = range(1, 10, 2)
  16. print(vect) # [1, 3, 5, 7, 9]
  17.  
  18. cdef vector[string] cpp_strings = b'It is a good shrubbery'.split()
  19. print(cpp_strings[1]) # b'is'

The following coercions are available:

Python type =>C++ type=> Python type
bytesstd::stringbytes
iterablestd::vectorlist
iterablestd::listlist
iterablestd::setset
iterable (len 2)std::pairtuple (len 2)

All conversions create a new container and copy the data into it.The items in the containers are converted to a corresponding typeautomatically, which includes recursively converting containersinside of containers, e.g. a C++ vector of maps of strings.

Iteration over stl containers (or indeed any class with begin() andend() methods returning an object supporting incrementing, dereferencing,and comparison) is supported via the for .. in syntax (including in listcomprehensions). For example, one can write:

  1. # distutils: language = c++
  2.  
  3. from libcpp.vector cimport vector
  4.  
  5. def main():
  6. cdef vector[int] v = [4, 6, 5, 10, 3]
  7.  
  8. cdef int value
  9. for value in v:
  10. print(value)
  11.  
  12. return [x*x for x in v if x % 2 == 0]

If the loop target variable is unspecified, an assignment from type*container.begin() is used for type inference.

Note

Slicing stl containers is supported,you can do for x in my_vector[:5]: … but unlike pointers slices,it will create a temporary Python object and iterate over it. Thusmaking the iteration very slow. You might want to avoid slicingC++ containers for performance reasons.

Simplified wrapping with default constructor

If your extension type instantiates a wrapped C++ class using the defaultconstructor (not passing any arguments), you may be able to simplify thelifecycle handling by tying it directly to the lifetime of the Python wrapperobject. Instead of a pointer attribute, you can declare an instance:

  1. # distutils: language = c++
  2.  
  3. from libcpp.vector cimport vector
  4.  
  5.  
  6. cdef class VectorStack:
  7. cdef vector[int] v
  8.  
  9. def push(self, x):
  10. self.v.push_back(x)
  11.  
  12. def pop(self):
  13. if self.v.empty():
  14. raise IndexError()
  15. x = self.v.back()
  16. self.v.pop_back()
  17. return x

Cython will automatically generate code that instantiates the C++ objectinstance when the Python object is created and deletes it when the Pythonobject is garbage collected.

Exceptions

Cython cannot throw C++ exceptions, or catch them with a try-except statement,but it is possible to declare a function as potentially raising an C++exception and converting it into a Python exception. For example,

  1. cdef extern from "some_file.h":
  2. cdef int foo() except +

This will translate try and the C++ error into an appropriate Python exception.The translation is performed according to the following table(the std:: prefix is omitted from the C++ identifiers):

C++Python
bad_allocMemoryError
bad_castTypeError
bad_typeidTypeError
domain_errorValueError
invalid_argumentValueError
ios_base::failureIOError
out_of_rangeIndexError
overflow_errorOverflowError
range_errorArithmeticError
underflow_errorArithmeticError
(all others)RuntimeError

The what() message, if any, is preserved. Note that a C++ios_base_failure can denote EOF, but does not carry enough informationfor Cython to discern that, so watch out with exception masks on IO streams.

  1. cdef int bar() except +MemoryError

This will catch any C++ error and raise a Python MemoryError in its place.(Any Python exception is valid here.)

  1. cdef int raise_py_error()
  2. cdef int something_dangerous() except +raise_py_error

If something_dangerous raises a C++ exception then raise_py_error will becalled, which allows one to do custom C++ to Python error “translations.” Ifraise_py_error does not actually raise an exception a RuntimeError will beraised.

There is also the special form:

  1. cdef int raise_py_or_cpp() except +*

for those functions that may raise either a Python or a C++ exception.

Static member method

If the Rectangle class has a static member:

  1. namespace shapes {
  2. class Rectangle {
  3. ...
  4. public:
  5. static void do_something();
  6.  
  7. };
  8. }

you can declare it using the Python @staticmethod decorator, i.e.:

  1. cdef extern from "Rectangle.h" namespace "shapes":
  2. cdef cppclass Rectangle:
  3. ...
  4. @staticmethod
  5. void do_something()

Declaring/Using References

Cython supports declaring lvalue references using the standard Type& syntax.Note, however, that it is unnecessary to declare the arguments of externfunctions as references (const or otherwise) as it has no impact on thecaller’s syntax.

auto Keyword

Though Cython does not have an auto keyword, Cython local variablesnot explicitly typed with cdef are deduced from the types of the right handside of all their assignments (see the infer_typescompiler directive). This is particularly handywhen dealing with functions that return complicated, nested, templated types,e.g.:

  1. cdef vector[int] v = ...
  2. it = v.begin()

(Though of course the for .. in syntax is preferred for objects supportingthe iteration protocol.)

RTTI and typeid()

Cython has support for the typeid(…) operator.


from cython.operator cimport typeid

The typeid(…) operator returns an object of the type const type_info &.

If you want to store a type_info value in a C variable, you will need to store itas a pointer rather than a reference:

  1. from libcpp.typeinfo cimport type_info
  2. cdef const type_info* info = &typeid(MyClass)

If an invalid type is passed to typeid, it will throw an std::bad_typeidexception which is converted into a TypeError exception in Python.

An additional C++11-only RTTI-related class, std::type_index, is availablein libcpp.typeindex.

Specify C++ language in setup.py

Instead of specifying the language and the sources in the source files, it ispossible to declare them in the setup.py file:

  1. from setuptools import setup
  2. from Cython.Build import cythonize
  3.  
  4. setup(ext_modules = cythonize(
  5. "rect.pyx", # our Cython source
  6. sources=["Rectangle.cpp"], # additional source file(s)
  7. language="c++", # generate C++ code
  8. ))

Cython will generate and compile the rect.cpp file (fromrect.pyx), then it will compile Rectangle.cpp(implementation of the Rectangle class) and link both object filestogether into rect.so on Linux, or rect.pyd on windows,which you can then import in Python usingimport rect (if you forget to link the Rectangle.o, you willget missing symbols while importing the library in Python).

Note that the language option has no effect on user provided Extensionobjects that are passed into cythonize(). It is only used for modulesfound by file name (as in the example above).

The cythonize() function in Cython versions up to 0.21 does notrecognize the language option and it needs to be specified as anoption to an Extension that describes your extension and thatis then handled by cythonize() as follows:

  1. from setuptools import Extension, setup
  2. from Cython.Build import cythonize
  3.  
  4. setup(ext_modules = cythonize(Extension(
  5. "rect", # the extension name
  6. sources=["rect.pyx", "Rectangle.cpp"], # the Cython source and
  7. # additional C++ source files
  8. language="c++", # generate and compile C++ code
  9. )))

The options can also be passed directly from the source file, which isoften preferable (and overrides any global option). Starting withversion 0.17, Cython also allows passing external source files into thecythonize() command this way. Here is a simplified setup.py file:

  1. from setuptools import setup
  2. from Cython.Build import cythonize
  3.  
  4. setup(
  5. name = "rectangleapp",
  6. ext_modules = cythonize('*.pyx'),
  7. )

And in the .pyx source file, write this into the first comment block, beforeany source code, to compile it in C++ mode and link it statically against theRectangle.cpp code file:

  1. # distutils: language = c++
  2. # distutils: sources = Rectangle.cpp

Note

When using distutils directives, the paths are relative to the workingdirectory of the setuptools run (which is usually the project root wherethe setup.py resides).

To compile manually (e.g. using make), the cython command-lineutility can be used to generate a C++ .cpp file, and then compile itinto a python extension. C++ mode for the cython command is turnedon with the —cplus option.

Caveats and Limitations

Access to C-only functions

Whenever generating C++ code, Cython generates declarations of and callsto functions assuming these functions are C++ (ie, not declared as extern "C"
{…}
. This is ok if the C functions have C++ entry points, but if they’re Conly, you will hit a roadblock. If you have a C++ Cython module needingto make calls to pure-C functions, you will need to write a small C++ shimmodule which:

  • includes the needed C headers in an extern “C” block
  • contains minimal forwarding functions in C++, each of which calls therespective pure-C function

C++ left-values

C++ allows functions returning a reference to be left-values. This is currentlynot supported in Cython. cython.operator.dereference(foo) is also notconsidered a left-value.