5

Edit: this answer solves the problem, but at the end I propose a more modular solution. Check it out :)

I know from two discussions that enum class should be chosen over enum for the sake of type safety. For this reason I'm using it in my projects, but when wrapping them with SWIG, I'm getting something that I don't quite like.

Given the simple enumeration

enum class type {
    A, B, C, D, E, F, G, H, I
};

in a .hpp file, say my_types.hpp, and the my_types.i file:

%module my_types
%{
#include "my_types.hpp"
%}
%include "my_types.hpp"

the generated python file contains contains

type_A = _my_types.type_A
type_B = _my_types.type_B
type_C = _my_types.type_C
type_D = _my_types.type_D
type_E = _my_types.type_E
type_F = _my_types.type_F
type_G = _my_types.type_G
type_H = _my_types.type_H
type_I = _my_types.type_I

which means that when using it in Python I will have to do

import my_types as mt
mt.type_A

to use them, which.... well, in this case is kind of okay, since the word type is not that long, but for longer words it won't look as good, and, also, to me, it does not make much sense to have the name of the enumeration at the beginning of each enumerated value.

Is there any way to rename them (perhaps using %rename) so that using them can be as simple as this?

import my_types as mt
mt.A

I don't know how to use %rename in this case, or it can be even used for this purpose. Any help, including ideas on how to do the same differently (maybe without enumerations), will be appreciated. Thank you all.

EDIT: some comments point out that having the enumeration's name in front of the name of each enumerated does make sense. I would like to elaborate my reasons why I said it does not (to me). If we consider the case of a module called just the same as the enumeration then the code looks a bit redundant. Since types is already a Python's module, a better example would be colours:

enum class colours {
    Blue, Red
};

and the wrapped code would be used, as suggested, like this:

import colours
Blue = colours.colours_Blue
Red = colours.colours_Red

It seems to me that writing colours to refer to Blue, Red is unnecessary since the module colours only contains one "enumeration". Wouldn't it be better to have this instead?

import colours
Blue = colours.Blue
Red = colours.Red

An answer has already explained on how to achieve this. I'll look at it as soon as I can. Thanks a lot!

Edit: this answer solves the problem. I would like to make a few changes to it and propose a slightly more "modular" solution:

%pythoncode %{
__enum_name = "colours"            # your enumeration's name
__enum = __import__(__enum_name)   # import module
__scope_name = __enum_name + "_"   # scope's name (= "colours_")
__scope_length = len(__scope_name) # length
for name in dir(__enum):
    if name.find(__scope_name) == 0:
        setattr(__enum, name[__scope_length:], getattr(__enum, name))
        delattr(__enum, name) # optional
del name, __enum_name, __enum, __scope_name, __scope_length
%}

I think this allows for easier copy and paste for your projects where you have tenths of enumerations :). Again, based on this answer.

  • "it does not make much sense to have the name of the enumeration at the beginning of each enumerated value." why? it is one of the benefits of `enum class` that you cannot refer to the first value by writing `A` but you have to write `type::A`. Thats why they are also called scoped enums, if you dont want it you can still use an unscoped enum – 463035818_is_not_an_ai Nov 13 '19 at 12:31
  • Certainly, `enum class` require you to write `type::A`, which makes the code even more readable. But the way I see it, in Python, if I have some module called type, which I have to import, having to write `type.type_A` is quite redundant, to me. So that is why I wanted to get rid of this. And simply have `type.A`. – Lluís Alemany-Puig Nov 13 '19 at 17:08
  • 1
    i didnt want to suggest "dont do it". Your edit made the problem much clearer – 463035818_is_not_an_ai Nov 13 '19 at 18:53

1 Answers1

4

I'll second that I don't completely understand why you would want to remove the scope (although I guess it is because in python you get the module scope already, something you don't have in C++). Now, I don't think %rename can be made to work, since it is not a textual transformation. I can think, however, of two alternatives, one renaming in C++, the other renaming in Python.

Rename in C++

Change my_types.hpp to:

enum class type {
    A, B, C, D, E, F, G, H, I
};

#ifdef SWIG
constexpr type A = type::A;
constexpr type B = type::B;
constexpr type C = type::C;
constexpr type D = type::D;
constexpr type E = type::E;
constexpr type F = type::F;
constexpr type G = type::G;
constexpr type H = type::H;
constexpr type I = type::I;
#endif

and compile the wrapper with -DSWIG added to the command line.

Rename in Python

Modify my_types.i to:

%module my_types
%{
#include "my_types.hpp"
%}
%include "my_types.hpp"

%pythoncode %{
import my_types
for name in dir(my_types):
    if name.find('type_') == 0:
       setattr(my_types, name[5:], getattr(my_types, name))
       delattr(my_types, name) # optional
del name
%}
Community
  • 1
  • 1
Wim Lavrijsen
  • 3,453
  • 1
  • 9
  • 21
  • "although I guess it is because in python you get the module scope already, something you don't have in C++". Yes, that's precisely why I don't quite like them. I've edited the question and added what should've been in the original question in the first place. – Lluís Alemany-Puig Nov 13 '19 at 17:47
  • I've added an extended answer to yours in the edit. Thanks a lot for your help! – Lluís Alemany-Puig Nov 14 '19 at 07:50