0

I have the following part of code in C:

typedef enum {
    CAN_NORMAL_MODE = 0x00,
    CAN_SLEEP_MODE = 0x01,
    CAN_INTERNAL_LOOPBACK_MODE = 0x02,
    CAN_LISTEN_ONLY_MODE = 0x03,
    CAN_CONFIGURATION_MODE = 0x04,
    CAN_EXTERNAL_LOOPBACK_MODE = 0x05,
    CAN_CLASSIC_MODE = 0x06,
    CAN_RESTRICTED_MODE = 0x07,
    CAN_INVALID_MODE = 0xFF
} CAN_OPERATION_MODE;

//more code here in the middle...

d = something 
switch (d) {
    case CAN_NORMAL_MODE:
        mode = CAN_NORMAL_MODE;
        break;
    case CAN_SLEEP_MODE:
        mode = CAN_SLEEP_MODE;
        break;
   // more cases here, not shown for readability
    default:
        mode = CAN_INVALID_MODE;
        break;
}

Here d is a byte that I read from other sources. The switch statement is inside another function, and more code is there, but I don't think it is necessary to show as it does nothing to do with this part.

My problem is: I'm trying to translate this code to Python, for reasons, so that it is sintactically similar to the original C code (that is, I don't want the Python code to be radically different, so another person that has worked with the original C code can understand and use easily the Python code). I'm not sure how to implement the functionality in this code snippet in Python language without doing an endless stream of if-elif...else statements. I think there should an easier (pythonic) way to do it, but I'm not sure how to implement the typedef, enum and switch statements in Python. I have read some ways to implement the enum clause creating a class with the aliases and values as attributes, like this:

class myClass():
    def __init__(self):
        self.CAN_NORMAL_MODE = 0x00
        self.CAN_SLEEP_MODE = 0x01
        self.CAN_INTERNAL_LOOPBACK_MODE = 0x02
    #etc

I have also come across a clever way of implementing a switch statement in Python, with a function like this (without any relation to my actual problem, just to show the structure of the implementation):

def switch(argument):
    switcher = {
        1: "January",
        2: "February",
        3: "March",
        4: "April",
        5: "May",
        6: "June",
        7: "July",
        8: "August",
        9: "September",
        10: "October",
        11: "November",
        12: "December"
    }
    print switcher.get(argument, "Invalid month")

But I can't come across a way of combining this two things in an easy and understandable way. Thank you and have a nice day!

jcf
  • 602
  • 1
  • 6
  • 26
  • `but I'm not sure how to implement the typedef, enum and switch statements in Python.` You don't – SuperStew Mar 20 '19 at 19:35
  • 4
    The C code looks like it can be replaced with `mode = (0x00 <= d && d <= 0x07) ? d : 0xFF`, which in Python would be `mode = d if 0x00 <= d <= 0x07 else 0xFF`. For the enum, use [`enum`](https://docs.python.org/3/library/enum.html#creating-an-enum) – Artyer Mar 20 '19 at 19:38
  • Related: [Does Python have an equivalent to switch?](https://stackoverflow.com/questions/1429505/does-python-have-an-equivalent-to-switch) – John Bollinger Mar 20 '19 at 19:50
  • If you have the C files, you can try [this answer](https://stackoverflow.com/a/66037988/208880) to generate the Python `Enum`s from them. – Ethan Furman Sep 20 '21 at 19:48

3 Answers3

2
from enum import IntEnum

class CAN_OPERATION_MODE(IntEnum):
    NORMAL_MODE = 0x00
    SLEEP_MODE = 0x01
    INTERNAL_LOOPBACK_MODE = 0x02
    LISTEN_ONLY_MODE = 0x03
    CONFIGURATION_MODE = 0x04
    EXTERNAL_LOOPBACK_MODE = 0x05
    CLASSIC_MODE = 0x06
    RESTRICTED_MODE = 0x07
    INVALID_MODE = 0xFF

    @classmethod
    def _missing_(cls, value):
        return cls.INVALID_MODE

d = something
mode = CAN_OPERATION_MODE(d)

I would also remove the _MODE from all the enum names.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
1

For the enum itself, you seem to be making it harder than it needs to be. An enum can be mapped very cleanly to a python module containing only a bunch of variable definitions, such as this:

can_operation_mode.py:

CAN_NORMAL_MODE = 0x00
CAN_SLEEP_MODE = 0x01
CAN_INTERNAL_LOOPBACK_MODE = 0x02
CAN_LISTEN_ONLY_MODE = 0x03
CAN_CONFIGURATION_MODE = 0x04
CAN_EXTERNAL_LOOPBACK_MODE = 0x05
CAN_CLASSIC_MODE = 0x06
CAN_RESTRICTED_MODE = 0x07
CAN_INVALID_MODE = 0xFF

That provides useful namespacing (which C enums don't), but also the ability to import those names into another module so as to use them C style, without module name. Of course, the tradeoff is that they are modifiable.

As for the switch, no, Python does not have a direct analog to C's switch, at least not if you disqualify the sort of if / else if / else construct you describe in the question:

other_module.py:

import can_operation_mode.*

d = something 
if   d == CAN_NORMAL_MODE:
    mode = CAN_NORMAL_MODE
elif d == CAN_SLEEP_MODE:
    mode = CAN_SLEEP_MODE
// more cases here ...
else:
    mode = CAN_INVALID_MODE

Frankly, though, I don't see what's wrong with that from a perspective of maintaining syntactic similarity to the original C. It looks pretty similar to me, right down to the use of colons.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • I was able to solve the problem in this way. I didn't want to do this kind of if-elif... construction, and I admit that I was a little obsessed with recreating the exact functionality of the C code in Python (instead of similar code that may work in a different way, but obtains the same results). Nevertheless, the most straightforward path (yours) was succesful. Thanks! – jcf Mar 21 '19 at 19:22
0

How about a dictionary? You could assign each value to the corresponding mode, then call it:

modes = {
    0x00 : 'CAN_NORMAL_MODE',
    0x01 : 'CAN_SLEEP_MODE',
    0x02 : 'CAN_INTERNAL_LOOPBACK_MODE',
    0x03 : 'CAN_LISTEN_ONLY_MODE',
    0x04 : 'CAN_CONFIGURATION_MODE',
    0x05 : 'CAN_EXTERNAL_LOOPBACK_MODE',
    0x06 : 'CAN_CLASSIC_MODE',
    0x07 : 'CAN_RESTRICTED_MODE',
    0xFF : 'CAN_INVALID_MODE'
}

d = something
mode = modes[d]
Merig
  • 1,751
  • 2
  • 13
  • 18