Commit 6c3f2815 authored by Robert Bradshaw's avatar Robert Bradshaw

Merge branch 'typeid'

parents d84f6476 eb5fc339
......@@ -42,6 +42,8 @@ Features added
* Some integer operations on Python long objects are faster in Python 2.7.
* Support for the C++ ``typeid`` operator.
Bugs fixed
----------
......
......@@ -67,7 +67,9 @@ class CythonScope(ModuleScope):
name_path = qname.split(u'.')
scope = self
while len(name_path) > 1:
scope = scope.lookup_here(name_path[0]).as_module
scope = scope.lookup_here(name_path[0])
if scope:
scope = scope.as_module
del name_path[0]
if scope is None:
return None
......
......@@ -6241,6 +6241,7 @@ class AttributeNode(ExprNode):
needs_none_check = True
is_memslice_transpose = False
is_special_lookup = False
is_py_attr = 0
def as_cython_attribute(self):
if (isinstance(self.obj, NameNode) and
......@@ -6670,7 +6671,7 @@ class AttributeNode(ExprNode):
else:
# result_code contains what is needed, but we may need to insert
# a check and raise an exception
if self.obj.type.is_extension_type:
if self.obj.type and self.obj.type.is_extension_type:
pass
elif self.entry and self.entry.is_cmethod:
# C method implemented as function call with utility code
......@@ -10213,6 +10214,8 @@ class SizeofTypeNode(SizeofNode):
def check_type(self):
arg_type = self.arg_type
if not arg_type:
return
if arg_type.is_pyobject and not arg_type.is_extension_type:
error(self.pos, "Cannot take sizeof Python object")
elif arg_type.is_void:
......@@ -10257,6 +10260,75 @@ class SizeofVarNode(SizeofNode):
def generate_result_code(self, code):
pass
class TypeidNode(ExprNode):
# C++ typeid operator applied to a type or variable
#
# operand ExprNode
# arg_type ExprNode
# is_variable boolean
type = PyrexTypes.error_type
subexprs = ['operand']
arg_type = None
is_variable = None
is_temp = 1
def get_type_info_type(self, env):
env_module = env
while not env_module.is_module_scope:
env_module = env_module.outer_scope
typeinfo_module = env_module.find_module('libcpp.typeinfo', self.pos)
typeinfo_entry = typeinfo_module.lookup('type_info')
return PyrexTypes.CFakeReferenceType(PyrexTypes.c_const_type(typeinfo_entry.type))
def analyse_types(self, env):
type_info = self.get_type_info_type(env)
if not type_info:
self.error("The 'libcpp.typeinfo' module must be cimported to use the typeid() operator")
return self
self.type = type_info
as_type = self.operand.analyse_as_type(env)
if as_type:
self.arg_type = as_type
self.is_type = True
else:
self.arg_type = self.operand.analyse_types(env)
self.is_type = False
if self.arg_type.type.is_pyobject:
self.error("Cannot use typeid on a Python object")
return self
elif self.arg_type.type.is_void:
self.error("Cannot use typeid on void")
return self
elif not self.arg_type.type.is_complete():
self.error("Cannot use typeid on incomplete type '%s'" % self.arg_type.type)
return self
env.use_utility_code(UtilityCode.load_cached("CppExceptionConversion", "CppSupport.cpp"))
return self
def error(self, mess):
error(self.pos, mess)
self.type = PyrexTypes.error_type
self.result_code = "<error>"
def check_const(self):
return True
def calculate_result_code(self):
return self.temp_code
def generate_result_code(self, code):
if self.is_type:
arg_code = self.arg_type.empty_declaration_code()
else:
arg_code = self.arg_type.result()
translate_cpp_exception(code, self.pos,
"%s = typeid(%s);" % (self.temp_code, arg_code),
None, self.in_nogil_context)
class TypeofNode(ExprNode):
# Compile-time type of an expression, as a string.
#
......
......@@ -635,6 +635,7 @@ class InterpretCompilerDirectives(CythonTransform, SkipDeclarations):
'operator.predecrement' : ExprNodes.inc_dec_constructor(True, '--'),
'operator.postincrement': ExprNodes.inc_dec_constructor(False, '++'),
'operator.postdecrement': ExprNodes.inc_dec_constructor(False, '--'),
'operator.typeid' : ExprNodes.TypeidNode,
# For backwards compatibility.
'address': ExprNodes.AmpersandNode,
......
from libcpp cimport bool
from .typeinfo cimport type_info
# This class is C++11-only
cdef extern from "<typeindex>" namespace "std" nogil:
cdef cppclass type_index:
type_index(const type_info &)
const char* name()
size_t hash_code()
bool operator==(const type_index &)
bool operator!=(const type_index &)
bool operator<(const type_index &)
bool operator<=(const type_index &)
bool operator>(const type_index &)
bool operator>=(const type_index &)
from libcpp cimport bool
cdef extern from "<typeinfo>" namespace "std" nogil:
cdef cppclass type_info:
const char* name()
int before(const type_info&)
bool operator==(const type_info&)
bool operator!=(const type_info&)
# C++11-only
size_t hash_code()
......@@ -167,6 +167,7 @@ memory_view_index: ':' [':'] [NUMBER]
address: '&' factor
cast: '<' type ['?'] '>' factor
size_of: 'sizeof' '(' (type) ')'
type_id: 'typeid' '(' (type) ')'
new_expr: 'new' type '(' [arglist] ')'
# TODO: Restrict cdef_stmt to "top-level" statements.
......
......@@ -18,6 +18,8 @@ static void __Pyx_CppExn2PyErr() {
PyErr_SetString(PyExc_MemoryError, exn.what());
} catch (const std::bad_cast& exn) {
PyErr_SetString(PyExc_TypeError, exn.what());
} catch (const std::bad_typeid& exn) {
PyErr_SetString(PyExc_TypeError, exn.what());
} catch (const std::domain_error& exn) {
PyErr_SetString(PyExc_ValueError, exn.what());
} catch (const std::invalid_argument& exn) {
......
......@@ -403,6 +403,9 @@ class __Pyx_FakeReference {
__Pyx_FakeReference(const T& ref) : ptr(const_cast<T*>(&ref)) { }
T *operator->() { return ptr; }
operator T&() { return *ptr; }
// TODO(robertwb): Delegate all operators (or auto-generate unwrapping code where needed).
template<typename U> bool operator ==(U other) { return *ptr == other; };
template<typename U> bool operator !=(U other) { return *ptr != other; };
private:
T *ptr;
};
......
......@@ -562,6 +562,8 @@ The translation is performed according to the following table
+-----------------------+---------------------+
| ``bad_cast`` | ``TypeError`` |
+-----------------------+---------------------+
| ``bad_typeid`` | ``TypeError`` |
+-----------------------+---------------------+
| ``domain_error`` | ``ValueError`` |
+-----------------------+---------------------+
| ``invalid_argument`` | ``ValueError`` |
......@@ -646,6 +648,27 @@ e.g.::
(Though of course the ``for .. in`` syntax is prefered for objects supporting
the 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 it
as a pointer rather than a reference:
from libcpp.typeinfo cimport type_info
cdef const type_info* info = &typeid(MyClass)
If an invalid type is passed to ``typeid``, it will throw an ``std::bad_typeid``
exception which is converted into a ``TypeError`` exception in Python.
An additional C++11-only RTTI-related class, ``std::type_index``, is available
in ``libcpp.typeindex``.
Caveats and Limitations
========================
......
......@@ -4,7 +4,7 @@
from cython cimport typeof
cimport cython.operator
from cython.operator cimport dereference as deref
from cython.operator cimport typeid, dereference as deref
from libc.string cimport const_char
from libcpp cimport bool
......@@ -50,12 +50,15 @@ cdef extern from "cpp_operators_helper.h":
const_char* operator[](int)
const_char* operator()(int)
cppclass TruthClass:
cdef cppclass TruthClass:
TruthClass()
TruthClass(bool)
bool operator bool()
bool value
cdef cppclass TruthSubClass(TruthClass):
pass
def test_unops():
"""
>>> test_unops()
......@@ -182,3 +185,39 @@ def test_bool_cond():
assert (TruthClass(False) and TruthClass(True)).value == False
assert (TruthClass(True) and TruthClass(False)).value == False
assert (TruthClass(True) and TruthClass(True)).value == True
ctypedef int* int_ptr
def test_typeid_op():
"""
>>> test_typeid_op()
"""
cdef TruthClass* test_1 = new TruthClass()
cdef TruthSubClass* test_2 = new TruthSubClass()
cdef TruthClass* test_3 = <TruthClass*> test_2
cdef TruthClass* test_4 = <TruthClass*> 0
assert typeid(TruthClass).name()
assert typeid(test_1).name()
assert typeid(TruthClass) == typeid(deref(test_1))
assert typeid(TruthSubClass).name()
assert typeid(test_2).name()
assert typeid(TruthSubClass) == typeid(deref(test_2))
assert typeid(TruthSubClass) == typeid(deref(test_3))
assert typeid(TruthClass) != typeid(deref(test_3))
assert typeid(TruthClass).name()
assert typeid(test_3).name()
assert typeid(TruthSubClass).name()
assert typeid(deref(test_2)).name()
assert typeid(int_ptr).name()
try:
typeid(deref(test_4))
assert False
except TypeError:
assert True
del test_1, test_2
......@@ -50,6 +50,7 @@ class TruthClass {
public:
TruthClass() : value(false) {}
TruthClass(bool value) : value(value) {}
virtual ~TruthClass() {};
operator bool() { return value; }
bool value;
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment