0

I'm specifically asking about C.

Example:

enum numbers {
    EVEN,
    ODD
};

int isFiveEvenOrOdd(void) {
    if (5 % 2 == ODD) return ODD;
    else return EVEN;
}

int main(void) {
    printf("%d", isFiveEvenOrOdd());
}

That code will return 1, because 5 mod 2 is ODD, and ODD is the second item in numbers (which means it has an index of one).

Is this a bad practice? Or should I change the return type of isFiveEvenOrOdd to the numbers enum.

typedef enum numbers numbers_t;

numbers_t isFiveEvenOrOdd(void) ...
New contributor
OnyxWingman is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
7
  • 3
    If you want your function to return only EVEN or ODD, why do you even think about returning it as an int? Any special reason? Will your code produce less compiler warnings?
    – Doc Brown
    Commented Jun 8 at 12:57
  • 1
    Because EVEN and ODD are not variables, but they are part of my enum numbers, therefore they are integers. That's my whole question, is it a bad practice to do so. Commented Jun 8 at 19:08
  • 2
    Before asking if "doing something X which might technically work, but looks unusual" is a bad practice, I would first answer for my self "why do I want do X". Otherwise I would not even consider doing X and the question about the bad practice would be pointless. And "because they are part of my enum numbers" is a reason to prefer an enum - just because "each enum is also an int, so technically an int is also allowed" is not a reason for using an int. So do you have a better reason why you think about using "int" as return type?
    – Doc Brown
    Commented Jun 9 at 8:05
  • 1
    Given that this is C, I would highly recommend explicitly defining EVEN to be 0 and ODD to be 1 if you are going to use ODD like you do in isFiveEvenOrOdd. Commented Jun 10 at 19:46
  • 1
    You're focusing on returning an enum as an integer; but I'm more concerned about you trying to use an integer as a return type for a method that clearly implies a boolean result. The enum almost doesn't matter, in that sense.
    – Flater
    Commented 2 days ago

4 Answers 4

5

Having some experience with a few languages, I would state: if you can make the code more readable for anyone (including yourself), do it without any hesitation, it is almost always the best way to go.

Coming to your code, I would say that at least a few things are quite hard for the audience. Going one by one, let's get to the first thing!

enum numbers {
    EVEN,
    ODD
};

What comes to your mind when you hear "numbers"?

  • a) all "numbers" as a set of natural, real or other numbers, pick one or more :)
  • b) 0, 1, 2, 3, ...
  • c) parity of the number

The most probable answer in my opinion is b), then a), and I would not consider c) as a valid answer. Why? That is a detail, extra information, not a number itself. Having it in point in mind, call it intuitively:

enum parity_of_number_t {
    EVEN,
    ODD
};

Now, let's move to the second part, which is a function.

int isFiveEvenOrOdd(void) {
    if (5 % 2 == ODD) return ODD;
    else return EVEN;
}

There are two ways in which we could modify the mentioned function to make it better. Let's work with the arbitrary number n.

// First option: change return type to bool (true/false) and rename
// function to isEven (or isOdd). Here no extra type is required.
bool isEven(int n) {
    if (n % 2 == 0) {
        return true;
    } else {
        return false;
    }
}

// Second option: use the parity_of_number type and rename
// the function accordingly.
parity_of_number_t getParityOfNumber(int n) {
    if (n % 2 == 0) {
        return EVEN;
    } else {
        return ODD;
    }
}

Which option is better? Sadly, the answer is: "it depends", and the part that it depends on is the place in code in which it is going to be used.

New contributor
whiskeyo is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
9
  • Is it better to make the return type parity_of_number_t? After all, it doesn't change anything, because enums are always ints. Why should we do this? For readability? Commented Jun 8 at 2:58
  • 1
    enums are not always ints; see this question on SO. Commented Jun 8 at 6:39
  • 3
    @OnyxWingman of course you should use enum as return type. While not perfect, it is better than raw int. Anyone who reads such signature understands for example what are the allowed values. Otherwise I would either need to read doc, assume something based on say function name, or read the source code. Of course under assumption that enums are used appropriately. The implicit conversion and the fact that any value can sit under enum is just one of many C/C++ horrors.
    – freakish
    Commented Jun 8 at 12:23
  • 2
    @Steve actually it is an understatement.
    – freakish
    Commented Jun 9 at 7:31
  • 1
    @OnyxWingman For readibility, yes. One really important thing to remember when writing code is that you're not just writing code so the compiler can understand it; you're also, or even mainly, writing code so coders (both other coders and future you) can see the meaning as easily as possible. Commented Jun 9 at 18:15
3

Using the enumerated type as the return type tells whomever is using this function that they can expect it to return one of exactly two values1; this gives them insight in how to best use this function and how to write their unit tests.

Using an int tells them it can return any of at least 2^16 values, positive or negative; you're telling them it can return EVEN (0), ODD (1), 42, -4321, 16575, etc. That gives them different insight on how to best use your function and test their code.

IOW you're lying to the user of your function. As with spouses and the IRS, this is generally a bad idea. If you are only ever going to return values of that enum type, then use that as the return type. If there's a possibility of returning a value that isn't in that type, then return an int and document it appropriately.

Having said that, C's enum is an incredibly weak abstraction; it's basically a way to introduce named integer constants and not much more than that. It's not as type safe as you might imagine. The compiler will not complain if you assign an enum numbers value to a regular int object or an object of a different enumeration type. It can be a useful abstraction, sometimes, but it's not going to protect you from mismatching types.

In this particular case I'd avoid it altogether and just have an isEven or isOdd that returns a Boolean value:

#include <stdbool.h>

bool isEven( int n )
{
  return n % 2 == 0;
}

bool isOdd( int n )
{
  return n % 2 != 0;
}

bool isFiveEven( void )
{
  return isEven( 5 ); // or just, you know, false
}

  1. Well, like I say later on enums are a weak abstraction, but again this indicates your intent more clearly than using a plain int.
0

You're asking a specific question, but there's several other points at play here that warrant more attention. I get the feeling that this is learner code (since you're hardcoding a 5), so I'll address these all as a point of learning.

enum numbers {
    EVEN,
    ODD
};

The name of the enum does not match its content. This enum does not list numbers, it lists the parity that numbers can have. Parity would be a more appropriate name here for an enum that contains Even and Odd.

if (5 % 2 == ODD) return ODD;
    else return EVEN;

Whenever you touch on code that effectively does if(x == myValue) return myValue;, you really need to consider why you need this if in the first place instead of just doing return x; without any need for a check in the first place.

This is the case in this scenario. The method body can be replaced by return 5 % 2;.

Subsequently, this proves that the enum does not really have a purpose here.

But, more importantly:

int isFiveEvenOrOdd(void) { }

Your question is focusing on whether you should be returning an integer or the enum type in this method. The short and simple answer is that neither is the right return type.

Methods that represent yes/no questions need a boolean return value, to indicate the yes or no response to the question. In the question, there's also no point to listing both options when they are mutually exclusive. If it's not one, then it's inherently the other.

This leads us to a method signature (and rename) along the lines of:

bool IsFiveEven() { }

If you prefer, you could've picked odd instead of even. Doesn't really make a difference, although you'll have to invert the logic that I'll write in a second.

I infer you're a learner, but I'll add this here already as a point of learning: get into the habit of not hardcoding things that don't make sense to be hardcoded. It makes no sense to hardcode 5 here. Design your methods with input parameters for these kinds of things.
This is technically not part of the question being asked, but it's part of method design 101, so it should have already factored into your design decision at this stage.

bool IsEven(int value) { }

Now that we've finetuned the design of the method (i.e. its signature), we can look at how we implement the logic that is required to achieve what the method is designed to do:

bool isEven(int value)
{
    return value % 2 == 0;
}

This code is much more cohesive in terms of what it represents:

  • The enum has been deprecated because it wasn't adding anything of value to your logic.
  • The return type matches the kind of information you will receive from this method (yes it is even / no it is not even)
  • The method has input rather than hardcoding its entire logic.
  • The method body avoids any checks that are unnecessary when it could simply return what it has without needing to first confirm it (and then return the same thing anyway)
0

In C/C++: If your cases are Boolean use bool. If your cases are really integers, like SecondsPerDay = 86400 use const int. If your cases are real cases return an enum.

The only time unsigned int is useful is when your cases are bits that should be combined like bit1 | bit2, or (1 << bit1) | (1 << bit2).

In Swift, it is impossible for an enum values to have other values than the ones in the enum definition (except if you use a library where an enum is specifically marked that it could have more values in the future).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.