Commit 2c7c22f5 authored by Ashwin Srinath's avatar Ashwin Srinath Committed by GitHub

Add support for C++ scoped enums with "enum class" and "enum struct" (GH-3640)

Closes #1603.
parent e87a5559
...@@ -2146,7 +2146,7 @@ class NameNode(AtomicExprNode): ...@@ -2146,7 +2146,7 @@ class NameNode(AtomicExprNode):
entry = self.entry entry = self.entry
if entry.is_type and entry.type.is_extension_type: if entry.is_type and entry.type.is_extension_type:
self.type_entry = entry self.type_entry = entry
if entry.is_type and entry.type.is_enum: if entry.is_type and (entry.type.is_enum or entry.type.is_cpp_enum):
py_entry = Symtab.Entry(self.name, None, py_object_type) py_entry = Symtab.Entry(self.name, None, py_object_type)
py_entry.is_pyglobal = True py_entry.is_pyglobal = True
py_entry.scope = self.entry.scope py_entry.scope = self.entry.scope
...@@ -6957,7 +6957,7 @@ class AttributeNode(ExprNode): ...@@ -6957,7 +6957,7 @@ class AttributeNode(ExprNode):
ubcm_entry.is_unbound_cmethod = 1 ubcm_entry.is_unbound_cmethod = 1
ubcm_entry.scope = entry.scope ubcm_entry.scope = entry.scope
return self.as_name_node(env, ubcm_entry, target=False) return self.as_name_node(env, ubcm_entry, target=False)
elif type.is_enum: elif type.is_enum or type.is_cpp_enum:
if self.attribute in type.values: if self.attribute in type.values:
for entry in type.entry.enum_values: for entry in type.entry.enum_values:
if entry.name == self.attribute: if entry.name == self.attribute:
......
...@@ -155,7 +155,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -155,7 +155,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.create_import_star_conversion_utility_code(env) self.create_import_star_conversion_utility_code(env)
for name, entry in sorted(env.entries.items()): for name, entry in sorted(env.entries.items()):
if (entry.create_wrapper and entry.scope is env if (entry.create_wrapper and entry.scope is env
and entry.is_type and entry.type.is_enum): and entry.is_type and (entry.type.is_enum or entry.type.is_cpp_enum)):
entry.type.create_type_wrapper(env) entry.type.create_type_wrapper(env)
def process_implementation(self, options, result): def process_implementation(self, options, result):
...@@ -880,7 +880,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -880,7 +880,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type = entry.type type = entry.type
if type.is_typedef: # Must test this first! if type.is_typedef: # Must test this first!
self.generate_typedef(entry, code) self.generate_typedef(entry, code)
elif type.is_enum: elif type.is_enum or type.is_cpp_enum:
self.generate_enum_definition(entry, code) self.generate_enum_definition(entry, code)
elif type.is_struct_or_union: elif type.is_struct_or_union:
self.generate_struct_union_definition(entry, code) self.generate_struct_union_definition(entry, code)
...@@ -957,8 +957,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -957,8 +957,6 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("#endif") code.putln("#endif")
code.putln(header) code.putln(header)
var_entries = scope.var_entries var_entries = scope.var_entries
if not var_entries:
error(entry.pos, "Empty struct or union definition not allowed outside a 'cdef extern from' block")
for attr in var_entries: for attr in var_entries:
code.putln( code.putln(
"%s;" % attr.type.declaration_code(attr.cname)) "%s;" % attr.type.declaration_code(attr.cname))
...@@ -1079,7 +1077,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1079,7 +1077,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.mark_pos(entry.pos) code.mark_pos(entry.pos)
type = entry.type type = entry.type
name = entry.cname or entry.name or "" name = entry.cname or entry.name or ""
header, footer = self.sue_header_footer(type, "enum", name)
kind = "enum class" if entry.type.is_cpp_enum else "enum"
header, footer = self.sue_header_footer(type, kind, name)
code.putln(header) code.putln(header)
enum_values = entry.enum_values enum_values = entry.enum_values
if not enum_values: if not enum_values:
...@@ -1093,18 +1093,20 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1093,18 +1093,20 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
for value_entry in enum_values: for value_entry in enum_values:
if value_entry.value_node is None: if value_entry.value_node is None:
value_code = value_entry.cname value_code = value_entry.cname.split("::")[-1]
else: else:
value_code = ("%s = %s" % ( value_code = ("%s = %s" % (
value_entry.cname, value_entry.cname.split("::")[-1],
value_entry.value_node.result())) value_entry.value_node.result()))
if value_entry is not last_entry: if value_entry is not last_entry:
value_code += "," value_code += ","
code.putln(value_code) code.putln(value_code)
code.putln(footer) code.putln(footer)
if entry.type.typedef_flag:
# Not pre-declared. if entry.type.is_enum:
code.putln("typedef enum %s %s;" % (name, name)) if entry.type.typedef_flag:
# Not pre-declared.
code.putln("typedef enum %s %s;" % (name, name))
def generate_typeobj_predeclaration(self, entry, code): def generate_typeobj_predeclaration(self, entry, code):
code.putln("") code.putln("")
......
...@@ -23,7 +23,7 @@ from . import TypeSlots ...@@ -23,7 +23,7 @@ from . import TypeSlots
from .PyrexTypes import py_object_type, error_type from .PyrexTypes import py_object_type, error_type
from .Symtab import (ModuleScope, LocalScope, ClosureScope, PropertyScope, from .Symtab import (ModuleScope, LocalScope, ClosureScope, PropertyScope,
StructOrUnionScope, PyClassScope, CppClassScope, TemplateScope, StructOrUnionScope, PyClassScope, CppClassScope, TemplateScope,
punycodify_name) CppScopedEnumScope, punycodify_name)
from .Code import UtilityCode from .Code import UtilityCode
from .StringEncoding import EncodedString from .StringEncoding import EncodedString
from . import Future from . import Future
...@@ -1535,36 +1535,56 @@ class CppClassNode(CStructOrUnionDefNode, BlockNode): ...@@ -1535,36 +1535,56 @@ class CppClassNode(CStructOrUnionDefNode, BlockNode):
class CEnumDefNode(StatNode): class CEnumDefNode(StatNode):
# name string or None # name string or None
# cname string or None # cname string or None
# items [CEnumDefItemNode] # scoped boolean Is a C++ scoped enum
# typedef_flag boolean # underlying_type CSimpleBaseTypeNode The underlying value type (int or C++ type)
# visibility "public" or "private" or "extern" # items [CEnumDefItemNode]
# api boolean # typedef_flag boolean
# in_pxd boolean # visibility "public" or "private" or "extern"
# create_wrapper boolean # api boolean
# entry Entry # in_pxd boolean
# create_wrapper boolean
child_attrs = ["items"] # entry Entry
child_attrs = ["items", "underlying_type"]
def declare(self, env): def declare(self, env):
self.entry = env.declare_enum( self.entry = env.declare_enum(
self.name, self.pos, self.name, self.pos,
cname=self.cname, typedef_flag=self.typedef_flag, cname=self.cname,
scoped=self.scoped,
typedef_flag=self.typedef_flag,
visibility=self.visibility, api=self.api, visibility=self.visibility, api=self.api,
create_wrapper=self.create_wrapper) create_wrapper=self.create_wrapper)
def analyse_declarations(self, env): def analyse_declarations(self, env):
scope = None
underlying_type = self.underlying_type.analyse(env)
if not underlying_type.is_int:
error(self.underlying_type.pos, "underlying type is not an integral type")
self.entry.type.underlying_type = underlying_type
if self.scoped and self.items is not None:
scope = CppScopedEnumScope(self.name, env)
scope.type = self.entry.type
else:
scope = env
if self.items is not None: if self.items is not None:
if self.in_pxd and not env.in_cinclude: if self.in_pxd and not env.in_cinclude:
self.entry.defined_in_pxd = 1 self.entry.defined_in_pxd = 1
for item in self.items: for item in self.items:
item.analyse_declarations(env, self.entry) item.analyse_declarations(scope, self.entry)
def analyse_expressions(self, env): def analyse_expressions(self, env):
return self return self
def generate_execution_code(self, code): def generate_execution_code(self, code):
if self.scoped:
return # nothing to do here for C++ enums
if self.visibility == 'public' or self.api: if self.visibility == 'public' or self.api:
code.mark_pos(self.pos) code.mark_pos(self.pos)
temp = code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=True) temp = code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=True)
...@@ -1596,9 +1616,15 @@ class CEnumDefItemNode(StatNode): ...@@ -1596,9 +1616,15 @@ class CEnumDefItemNode(StatNode):
if not self.value.type.is_int: if not self.value.type.is_int:
self.value = self.value.coerce_to(PyrexTypes.c_int_type, env) self.value = self.value.coerce_to(PyrexTypes.c_int_type, env)
self.value = self.value.analyse_const_expression(env) self.value = self.value.analyse_const_expression(env)
if enum_entry.type.is_cpp_enum:
cname = "%s::%s" % (enum_entry.cname, self.name)
else:
cname = self.cname
entry = env.declare_const( entry = env.declare_const(
self.name, enum_entry.type, self.name, enum_entry.type,
self.value, self.pos, cname=self.cname, self.value, self.pos, cname=cname,
visibility=enum_entry.visibility, api=enum_entry.api, visibility=enum_entry.visibility, api=enum_entry.api,
create_wrapper=enum_entry.create_wrapper and enum_entry.name is None) create_wrapper=enum_entry.create_wrapper and enum_entry.name is None)
enum_entry.enum_values.append(entry) enum_entry.enum_values.append(entry)
......
...@@ -3133,6 +3133,12 @@ def p_cdef_extern_block(s, pos, ctx): ...@@ -3133,6 +3133,12 @@ def p_cdef_extern_block(s, pos, ctx):
def p_c_enum_definition(s, pos, ctx): def p_c_enum_definition(s, pos, ctx):
# s.sy == ident 'enum' # s.sy == ident 'enum'
s.next() s.next()
scoped = False
if s.context.cpp and (s.sy == 'class' or (s.sy == 'IDENT' and s.systring == 'struct')):
scoped = True
s.next()
if s.sy == 'IDENT': if s.sy == 'IDENT':
name = s.systring name = s.systring
s.next() s.next()
...@@ -3140,24 +3146,49 @@ def p_c_enum_definition(s, pos, ctx): ...@@ -3140,24 +3146,49 @@ def p_c_enum_definition(s, pos, ctx):
if cname is None and ctx.namespace is not None: if cname is None and ctx.namespace is not None:
cname = ctx.namespace + "::" + name cname = ctx.namespace + "::" + name
else: else:
name = None name = cname = None
cname = None if scoped:
items = None s.error("Unnamed scoped enum not allowed")
if scoped and s.sy == '(':
s.next()
underlying_type = p_c_base_type(s)
s.expect(')')
else:
underlying_type = Nodes.CSimpleBaseTypeNode(
pos,
name="int",
module_path = [],
is_basic_c_type = True,
signed = 1,
complex = 0,
longness = 0
)
s.expect(':') s.expect(':')
items = [] items = []
if s.sy != 'NEWLINE': if s.sy != 'NEWLINE':
p_c_enum_line(s, ctx, items) p_c_enum_line(s, ctx, items)
else: else:
s.next() # 'NEWLINE' s.next() # 'NEWLINE'
s.expect_indent() s.expect_indent()
while s.sy not in ('DEDENT', 'EOF'): while s.sy not in ('DEDENT', 'EOF'):
p_c_enum_line(s, ctx, items) p_c_enum_line(s, ctx, items)
s.expect_dedent() s.expect_dedent()
if not items and ctx.visibility != "extern":
error(pos, "Empty enum definition not allowed outside a 'cdef extern from' block")
return Nodes.CEnumDefNode( return Nodes.CEnumDefNode(
pos, name = name, cname = cname, items = items, pos, name=name, cname=cname,
typedef_flag = ctx.typedef_flag, visibility = ctx.visibility, scoped=scoped, items=items,
create_wrapper = ctx.overridable, underlying_type=underlying_type,
api = ctx.api, in_pxd = ctx.level == 'module_pxd') typedef_flag=ctx.typedef_flag, visibility=ctx.visibility,
create_wrapper=ctx.overridable,
api=ctx.api, in_pxd=ctx.level == 'module_pxd')
def p_c_enum_line(s, ctx, items): def p_c_enum_line(s, ctx, items):
if s.sy != 'pass': if s.sy != 'pass':
...@@ -3217,8 +3248,12 @@ def p_c_struct_or_union_definition(s, pos, ctx): ...@@ -3217,8 +3248,12 @@ def p_c_struct_or_union_definition(s, pos, ctx):
s.next() s.next()
s.expect_newline("Expected a newline") s.expect_newline("Expected a newline")
s.expect_dedent() s.expect_dedent()
if not attributes and ctx.visibility != "extern":
error(pos, "Empty struct or union definition not allowed outside a 'cdef extern from' block")
else: else:
s.expect_newline("Syntax error in struct or union definition") s.expect_newline("Syntax error in struct or union definition")
return Nodes.CStructOrUnionDefNode(pos, return Nodes.CStructOrUnionDefNode(pos,
name = name, cname = cname, kind = kind, attributes = attributes, name = name, cname = cname, kind = kind, attributes = attributes,
typedef_flag = ctx.typedef_flag, visibility = ctx.visibility, typedef_flag = ctx.typedef_flag, visibility = ctx.visibility,
......
...@@ -182,6 +182,7 @@ class PyrexType(BaseType): ...@@ -182,6 +182,7 @@ class PyrexType(BaseType):
# is_struct_or_union boolean Is a C struct or union type # is_struct_or_union boolean Is a C struct or union type
# is_struct boolean Is a C struct type # is_struct boolean Is a C struct type
# is_enum boolean Is a C enum type # is_enum boolean Is a C enum type
# is_cpp_enum boolean Is a C++ scoped enum type
# is_typedef boolean Is a typedef type # is_typedef boolean Is a typedef type
# is_string boolean Is a C char * type # is_string boolean Is a C char * type
# is_pyunicode_ptr boolean Is a C PyUNICODE * type # is_pyunicode_ptr boolean Is a C PyUNICODE * type
...@@ -248,6 +249,7 @@ class PyrexType(BaseType): ...@@ -248,6 +249,7 @@ class PyrexType(BaseType):
is_cpp_string = 0 is_cpp_string = 0
is_struct = 0 is_struct = 0
is_enum = 0 is_enum = 0
is_cpp_enum = False
is_typedef = 0 is_typedef = 0
is_string = 0 is_string = 0
is_pyunicode_ptr = 0 is_pyunicode_ptr = 0
...@@ -4019,6 +4021,73 @@ class CppClassType(CType): ...@@ -4019,6 +4021,73 @@ class CppClassType(CType):
if constructor is not None and best_match([], constructor.all_alternatives()) is None: if constructor is not None and best_match([], constructor.all_alternatives()) is None:
error(pos, "C++ class must have a nullary constructor to be %s" % msg) error(pos, "C++ class must have a nullary constructor to be %s" % msg)
class CppScopedEnumType(CType):
# name string
# cname string
is_cpp_enum = True
def __init__(self, name, cname, underlying_type, namespace=None):
self.name = name
self.cname = cname
self.values = []
self.underlying_type = underlying_type
self.namespace = namespace
def __str__(self):
return self.name
def declaration_code(self, entity_code,
for_display=0, dll_linkage=None, pyrex=0):
if pyrex or for_display:
type_name = self.name
else:
if self.namespace:
type_name = "%s::%s" % (
self.namespace.empty_declaration_code(),
self.cname
)
else:
type_name = "enum %s" % self.cname
type_name = public_decl(type_name, dll_linkage)
return self.base_declaration_code(type_name, entity_code)
def create_from_py_utility_code(self, env):
if self.from_py_function:
return True
if self.underlying_type.create_from_py_utility_code(env):
self.from_py_function = '(%s)%s' % (
self.cname, self.underlying_type.from_py_function
)
return True
def create_to_py_utility_code(self, env):
if self.to_py_function is not None:
return True
if self.underlying_type.create_to_py_utility_code(env):
# Using a C++11 lambda here, which is fine since
# scoped enums are a C++11 feature
self.to_py_function = '[](const %s& x){return %s((%s)x);}' % (
self.cname,
self.underlying_type.to_py_function,
self.underlying_type.empty_declaration_code()
)
return True
def create_type_wrapper(self, env):
from .UtilityCode import CythonUtilityCode
rst = CythonUtilityCode.load(
"CppScopedEnumType", "CpdefEnums.pyx",
context={
"name": self.name,
"cname": self.cname.split("::")[-1],
"items": tuple(self.values),
"underlying_type": self.underlying_type.empty_declaration_code(),
},
outer_module_scope=env.global_scope())
env.use_utility_code(rst)
class TemplatePlaceholderType(CType): class TemplatePlaceholderType(CType):
......
...@@ -679,8 +679,8 @@ class Scope(object): ...@@ -679,8 +679,8 @@ class Scope(object):
error(pos, "'%s' previously declared as '%s'" % ( error(pos, "'%s' previously declared as '%s'" % (
entry.name, entry.visibility)) entry.name, entry.visibility))
def declare_enum(self, name, pos, cname, typedef_flag, def declare_enum(self, name, pos, cname, scoped, typedef_flag,
visibility = 'private', api = 0, create_wrapper = 0): visibility='private', api=0, create_wrapper=0):
if name: if name:
if not cname: if not cname:
if (self.in_cinclude or visibility == 'public' if (self.in_cinclude or visibility == 'public'
...@@ -692,13 +692,18 @@ class Scope(object): ...@@ -692,13 +692,18 @@ class Scope(object):
namespace = self.outer_scope.lookup(self.name).type namespace = self.outer_scope.lookup(self.name).type
else: else:
namespace = None namespace = None
type = PyrexTypes.CEnumType(name, cname, typedef_flag, namespace)
if scoped:
type = PyrexTypes.CppScopedEnumType(name, cname, namespace)
else:
type = PyrexTypes.CEnumType(name, cname, typedef_flag, namespace)
else: else:
type = PyrexTypes.c_anon_enum_type type = PyrexTypes.c_anon_enum_type
entry = self.declare_type(name, type, pos, cname = cname, entry = self.declare_type(name, type, pos, cname = cname,
visibility = visibility, api = api) visibility = visibility, api = api)
entry.create_wrapper = create_wrapper entry.create_wrapper = create_wrapper
entry.enum_values = [] entry.enum_values = []
self.sue_entries.append(entry) self.sue_entries.append(entry)
return entry return entry
...@@ -2628,6 +2633,22 @@ class CppClassScope(Scope): ...@@ -2628,6 +2633,22 @@ class CppClassScope(Scope):
return scope return scope
class CppScopedEnumScope(Scope):
# Namespace of a ScopedEnum
def __init__(self, name, outer_scope):
Scope.__init__(self, name, outer_scope, None)
def declare_var(self, name, type, pos,
cname=None, visibility='extern'):
# Add an entry for an attribute.
if not cname:
cname = name
entry = self.declare(name, cname, type, pos, visibility)
entry.is_variable = True
return entry
class PropertyScope(Scope): class PropertyScope(Scope):
# Scope holding the __get__, __set__ and __del__ methods for # Scope holding the __get__, __set__ and __del__ methods for
# a property of an extension type. # a property of an extension type.
......
...@@ -60,3 +60,21 @@ else: ...@@ -60,3 +60,21 @@ else:
{{for item in items}} {{for item in items}}
__Pyx_globals['{{item}}'] = {{name}}({{item}}, '{{item}}') __Pyx_globals['{{item}}'] = {{name}}({{item}}, '{{item}}')
{{endfor}} {{endfor}}
#################### CppScopedEnumType ####################
#@requires: EnumBase
cdef dict __Pyx_globals = globals()
if PY_VERSION_HEX >= 0x03040000:
# create new IntEnum()
__Pyx_globals["{{name}}"] = __Pyx_EnumBase('{{name}}', __Pyx_OrderedDict([
{{for item in items}}
('{{item}}', <{{underlying_type}}>({{name}}.{{item}})),
{{endfor}}
]))
else:
__Pyx_globals["{{name}}"] = type('{{name}}', (__Pyx_EnumBase,), {})
{{for item in items}}
__Pyx_globals["{{name}}"](<{{underlying_type}}>({{name}}.{{item}}), '{{item}}')
{{endfor}}
...@@ -482,6 +482,33 @@ Note, however, that it is unnecessary to declare the arguments of extern ...@@ -482,6 +482,33 @@ Note, however, that it is unnecessary to declare the arguments of extern
functions as references (const or otherwise) as it has no impact on the functions as references (const or otherwise) as it has no impact on the
caller's syntax. caller's syntax.
Scoped Enumerations
-------------------
Cython supports scoped enumerations (:keyword:`enum class`) in C++ mode::
cdef enum class Cheese:
cheddar = 1
camembert = 2
As with "plain" enums, you may access the enumerators as attributes of the type.
Unlike plain enums however, the enumerators are not visible to the
enclosing scope::
cdef Cheese c1 = Cheese.cheddar # OK
cdef Cheese c2 = cheddar # ERROR!
Optionally, you may specify the underlying type of a scoped enumeration.
This is especially important when declaring an external scoped enumeration
with an underlying type::
cdef extern from "Foo.h":
cdef enum class Spam(unsigned int):
x = 10
y = 20
...
Declaring an enum class as ``cpdef`` will create a :pep:`435`-style Python wrapper.
``auto`` Keyword ``auto`` Keyword
---------------- ----------------
......
# mode: compile
# tag: cpp,cpp11
cpdef enum class Spam:
a, b
c
d
e
f = 42
cpdef enum class Cheese(unsigned int):
x = 1
y = 2
cdef enum struct parrot_state:
alive = 1
dead = 0
cdef void eggs():
cdef Spam s1
s1 = Spam.a
s2 = Spam.b
cdef Cheese c1
c1 = Cheese.x
eggs()
# mode: error
# tag: cpp
cdef enum class Spam:
a
cdef enum class Spam:
b
_ERRORS="""
7:5: 'Spam' redeclared
4:5: Previous declaration is here
"""
# mode: error
cdef enum Spam(int):
a, b
_ERRORS = u"""
3:14: Expected ':', found '('
"""
# mode: run
# tag: cpp, cpp11
cdef extern from *:
"""
enum class Enum1 {
Item1 = 1,
Item2 = 2
};
"""
cpdef enum class Enum1:
Item1
Item2
def test_enum_to_list():
"""
>>> test_enum_to_list()
"""
assert list(Enum1) == [1, 2]
# mode: run
# tag: cpp, cpp11
"""
PYTHON setup.py build_ext --inplace
PYTHON -c "import runner"
"""
######## setup.py ########
from Cython.Build.Dependencies import cythonize
from distutils.core import setup
setup(ext_modules=cythonize("*.pyx", language='c++'))
setup(
ext_modules = cythonize([
"cheese.pyx",
"import_scoped_enum_test.pyx",
"dotted_import_scoped_enum_test.pyx"
])
)
######## cheese.pxd ########
# distutils: language = c++
# distutils: extra_compile_args = -std=c++11
cdef extern from * namespace "Namespace":
"""
namespace Namespace {
enum class Cheese {
cheddar = 1,
camembert = 2
};
}
"""
cpdef enum class Cheese:
cheddar
camembert
######## cheese.pyx ########
# distutils: language = c++
# distutils: extra_compile_args = -std=c++11
pass
######## import_scoped_enum_test.pyx ########
# distutils: language = c++
# distutils: extra_compile_args = -std=c++11
from cheese import Cheese
from cheese cimport Cheese
cdef Cheese c = Cheese.cheddar
assert list(Cheese) == [1, 2]
######## dotted_import_scoped_enum_test.pyx ########
# distutils: language = c++
# distutils: extra_compile_args = -std=c++11
cimport cheese
cdef cheese.Cheese c = cheese.Cheese.cheddar
assert [cheese.Cheese.cheddar, cheese.Cheese.camembert] == [1, 2]
cdef cheese.Cheese d = int(1)
######## runner.py ########
import import_scoped_enum_test
import dotted_import_scoped_enum_test
# mode: run
# tag: cpp, cpp11
from libcpp.limits cimport numeric_limits
cdef extern from *:
"""
enum class Enum1 {
Item1,
Item2
};
"""
cdef enum class Enum1:
Item1
Item2
cdef extern from * namespace "Namespace1":
"""
namespace Namespace1 {
enum class Enum2 {
Item1,
Item2
};
}
"""
cdef enum class Enum2:
Item1
Item2
cdef enum class Enum3(int):
a = 1
b = 2
cdef extern from *:
"""
enum class sorted
{
a = 1,
b = 0
};
"""
cdef enum class Enum4 "sorted":
a
b
cdef extern from *:
"""
#include <limits>
enum class LongIntEnum : long int {
val = std::numeric_limits<long int>::max(),
};
"""
enum class LongIntEnum(long int):
val
def test_compare_enums():
"""
>>> test_compare_enums()
(True, True, False, False)
"""
cdef Enum1 x, y
x = Enum1.Item1
y = Enum1.Item2
return (
x == Enum1.Item1,
y == Enum1.Item2,
x == Enum1.Item2,
y == Enum1.Item1
)
def test_compare_namespace_enums():
"""
>>> test_compare_enums()
(True, True, False, False)
"""
cdef Enum2 z, w
z = Enum2.Item1
w = Enum2.Item2
return (
z == Enum2.Item1,
w == Enum2.Item2,
z == Enum2.Item2,
w == Enum2.Item1
)
def test_coerce_to_from_py_value(object i):
"""
>>> test_coerce_to_from_py_value(1)
(True, False)
>>> test_coerce_to_from_py_value(2)
(False, True)
>>> test_coerce_to_from_py_value(3)
(False, False)
>>> test_coerce_to_from_py_value(11111111111111111111111111111111111111111111)
Traceback (most recent call last):
OverflowError: Python int too large to convert to C long
"""
cdef Enum3 x = i
y = Enum3.b
return (
x == Enum3.a,
y == int(i)
)
def test_reserved_cname():
"""
>>> test_reserved_cname()
True
"""
cdef Enum4 x = Enum4.a
return Enum4.a == int(1)
def test_large_enum():
"""
>>> test_large_enum()
True
"""
long_max = int(numeric_limits[long].max())
return LongIntEnum.val == long_max
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