3

Cython doesn't seem to allow me to reuse enum member names.

I have the following enums that I'm trying to cythonize:

from enum import Enum

class Fruit(Enum):

    UNKNOWN = 0
    APPLE = 1
    ORANGE = 2

class Animal(Enum):

    UNKNOWN = 0
    DOG = 1
    CAT = 2

But the following results in a 'UNKNOWN' redeclared compilation error:

cpdef enum Fruit:

    UNKNOWN = 0
    APPLE = 1
    ORANGE = 2    

cpdef enum Animal:

    UNKNOWN = 0
    DOG = 1
    CAT = 

How should I fix the above?

Also, I'd ideally like to use NULL as the enum member names instead of UNKNOWN. But it seems that NULL is a privileged keyword for Cython though it's not for CPython. Is there any workaround?

Katie
  • 918
  • 1
  • 5
  • 18

1 Answers1

3

In C, the following code would not compile:

enum Foo{
    A
};

enum Bar{
    A
};

because in C, enums don’t introduce a new namescope and thus a name clash happens if you use the same identifier A for both types Foo and Bar.

Cython would translate (if it would just apply its usual scheme and not stop with a compiler-error):

#foo.pyx
cpdef enum Fruit:
    UNKNOWN = 0   

cpdef enum Animal:
    UNKNOWN = 0

to something like

enum __pyx_t_3foo_Fruit {
  __pyx_e_3foo_UNKNOWN = 0
};

enum __pyx_t_3foo_Animal {
  __pyx_e_3foo_UNKNOWN = 0
};

and as we have seen, this is not valid C, so Cython's choice is to generate an error. A "problem" is that Cython doesn't include the type name (Fruit, Animal) into the generated enum-value-name, so while in Cython you would be able to differentiate between them as Fruit.UNKNOWN and Animal.UNKNOWN, they would map to the same C-identifier.

There are at least two options:

A: The usual strategy in C is to use a prefix to differentiate between the enums, e.g.:

cpdef enum Fruit:
       FRUIT_UNKNOWN = 0

cpdef enum Animal:
      ANIMAL_UNKNOWN = 1

or B: you can let Cython to do this work by putting them in different pxd-files, which are imported in the resulting pyx-file:

# Fruit.pxd
cpdef enum Fruit:
    UNKNOWN = 0

#Animal.pxd
cpdef enum Animal:
    UNKNOWN = 0

# foo.pyx:
cimport Fruit
cimport Animal

# use Fruit.UNKNOWN and Animal.UNKNOWN

in the resulting code, the enum-names will have the module names (i.e. Fruit and Animal) and thus are not the same from C-compiler's point of view.

ead
  • 32,758
  • 6
  • 90
  • 153
  • Thanks for the answer. I'd imagine there's some hacky way to replicate the Python version if it works fine on CPython? I'll accept your answer in 2 weeks if there's no better approach. – Katie Jun 02 '18 at 15:49