Question regarding program organization and using the enum class

Hey, so I'm working on a personal project that needs to expand well and work as a base for a lot of other possible programs. I've organized several classes, but realized that there's a few unrelated functions and constants many of them share. Given that several of the classes are needing to be stored as objects in a collection, I've decided those functions need to be accessible globally (instead of copied endlessly).

The functions and constants aren't strongly related to each other, so they don't 'fit' nicely in a class (or as a specific 'type'). My solution then was to put the constants, functions, and enums in a header file that defines a namespace for them. The namespace mostly is used to make the usage of those functions/constants explicitly clear in my code, and to keep them all in one place.

So my question is, is this an acceptable approach? Is there a better alternative?


And my question on the enum class:
Within that namespace, are all of my enums... and there's going to be a lot of them. What I need from those enum classes, is to be able to easily convert them back and forth to strings, to define their 'type,' and to be able to compare enums of the same type to each other as ints.

I've looked at both the standard C enum versus the enum class. Given how their type is really important, I'm thinking the enum class is the way to go. So what's the best approach to representing them as strings? Currently I've been using a switch case, but that seems like an excessive way to go about it given that I'll likely have at least 50ish or so enums to do this with. Is there a better alternative?

Secondly, I need to compare them as ints. I understand I'll have to write a function to explicitly convert them. What I'm wondering is, does the same short-cut used with the C enum convention for assigning consecutive int values work for the enum class? Something like this:
1
2
3
 enum class names{
     FIRST_THING = 0, SECOND_THING, THIRD_THING
}
enums don't contain strings at all. Strings are objects which contain loads of chars, enums basically tell the compiler, if you see this, replace it with this number. You can use a map (or unordered_map) which maps numbers to strings.
Regarding the implicit casting to int, I thought the purpose of the 'enum class' was that it didn't implicitly convert to int, but instead was treated as it's own type, thus requiring an explicit cast if you needed an int value? If this is the case, wouldn't using a map still require something like a switch-case statement for every enum in order to initially convert it to either a string or int?
The point is you don't use an enum if what you want is conversion between int and string. And you can convert to int using a conversion function static_cast<int>(yourEnumHere) or int(youEnumHere)
We have to convert strings to/from various enums all over the place so we have a pretty lightweight class that lets you define and use the mapping. The disadvantage is that it does a linear search, but since we only do it to convert to/from human consumption, which is rare compared to the computer usage of the enums, it doesn't matter.

The class is sort of like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
class StringMap {
public:
    StringMap(Data *d) :
        array(d) {}
    class Data {
        long val;
        const char *str;
    }
    long stringToLong(const char *s);  // lookup number for string
    const char *longToString(long val);  // lookup string for number
private:
    Data *array;  // not owned by this
};


You create a map like this:
1
2
3
4
5
6
7
StringMap::Data numMapData[] = {
    {1, "one"},
    {2, "two"},
    {3, "three" }
    {0, nullptr }  // nullptr in string indicates end of array
};
StringMap numberMap(numMapData);



Be careful about using an STL container for this. If you have 500 enums and create std::maps for each of them, I suspect you'll end up with 500 copies of the code. In an experiment a few months ago, I showed that a single std::map added 15k to the program size.
Shadowmouse: Oh!! Wow, yeah I see what you're saying. That actually does seem like a better approach to the problem (once I started hard coding the conversions, I began realizing how much overhead there was). Just to clarify, by creating a map instead, I can still use those values internally as if they were enums right?


dhayden: Thanks for the code snippet! So with that solution, is there a need for enums at all? The computer will obviously deal with the problems I'm presenting mathematically while, as you pointed out, the human readability side of this is much less frequent. Isn't all the information represented directly in the map itself both for the computer and humans?

Though it's entirely possible I'm not fully understanding your code.
What exactly are you trying to use the enums for? When you said strings did you in fact mean text within the code that represents a number value (the only thing enums can do, although it can be used in various ways), or were you wanting to convert from a number to a string that can be output by the program?
So with that solution, is there a need for enums at all?

That bit of code just converts numbers to/from strings. Whether you need enums is a different story.

Let's get back to the original question:
Within that namespace, are all of my enums... and there's going to be a lot of them. What I need from those enum classes, is to be able to easily convert them back and forth to strings, to define their 'type,' and to be able to compare enums of the same type to each other as ints.

Can you give an example of what you need to do? When you say you need to compare enums of the same type as int, do you mean:
1
2
3
4
5
6
7
enum A { a, b, c};
enum B {d, e, f};

enum A var1;
enum B var2;
...
if ((int)var1 == (int)var2) {....


If you're frequently comparing the values of two different types of enums then perhaps enums aren't the best choice. Maybe const int or constexpr int would be better.
FWIW

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <string>

#include "enum.h" // http://aantron.github.io/better-enums/index.html

namespace test 
{ 
    ENUM( colour, int, RED = 1, GREEN = 2, BLUE = 4, BLACK = 0, WHITE = RED|GREEN|BLUE ) 
}

/*
struct broken 
{ 
    ENUM( colour, int, RED = 1, GREEN = 2, BLUE = 4, BLACK = 0, WHITE = RED|GREEN|BLUE )
};
*/

int main()
{
    const test::colour clr = test::colour::BLUE ;
    std::cout << clr << '\n' ; // BLUE
    const std::string clr_name = std::string("test::colour::") + clr._to_string() ;
    std::cout << clr_name << '\n' ; // test::colour::BLUE
    
    const auto clr2 = test::colour::_from_string( "WHITE" ) ;
    std::cout << clr2 << '\n' ; // WHITE
    
    test::colour clr3 = test::colour::RED ;
    if( std::cin >> clr3 ) /* enter "GREEN" */ std::cout << clr3 << '\n' ; // GREEN
}


uname -a
echo && clang++ --version | grep clang && echo && clang++ -std=c++14 -stdlib=libc++ -O3 -Wall -Wextra -pedantic-errors main.cpp && ./a.out <<< GREEN
echo && g++ --version | grep GCC && echo && g++ -std=c++14 -O3 -Wall -Wextra -pedantic-errors main.cpp && ./a.out  <<< GREEN
Linux stacked-crooked 3.2.0-74-virtual #109-Ubuntu SMP Tue Dec 9 17:04:48 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

clang version 3.6.0 (tags/RELEASE_360/final 235480)

BLUE
test::colour::BLUE
WHITE
GREEN

g++ (GCC) 5.2.0

BLUE
test::colour::BLUE
WHITE
GREEN

http://coliru.stacked-crooked.com/a/c901a51665ab24fd
To clarify the program (which should address both Shadowmouse and dhayden's response), let me go ahead and clarify what I'm doing:

The base part of the program basically organizes and analyzes music theory for the purpose of composition. The program itself only needs to think of music theory in terms of math, so individual notes, scales, and chords can all be numbers as far as the program is concerned. As a non-programmer/user of the program, I would need to be able to type in note names as strings, and have the program output the notes it's generated as strings. As someone building other programs that use the code, I need to be able to use note names as arguments.

One important feature, is being able to compare one note against another note. When two notes are compared, the only thing that matters are their pitches. So I want to compare if one note is the same pitch as another, higher, or lower.

My goal in using the enums, was to ensure a common language to describe the notes being used. Being able to seamlessly describe them as a string, a type, or an int, is my intent as the way I've approached the problem requires that. So now I suppose the question is, 'is this the best approach?'

The type becomes important when comparing them as I have other enums describing scale types and chord types. A note is not a chord or scale, and so should never be compared as such. That was my thinking anyway. So in your example dhayden, I would want to ensure that members of enum A can't be compared to members of enum B.
I would personally make a note class which both has a name (string) and a pitch (double/int) and then have a map of string to double/int for converting inputted note names into pitches, but that is just one way of doing it.
I agree with shadowmouse. You could represent pitch in hertz. After all some notes are flat, some are sharp, some a little off key. A note has pitch, duration(ms?), volume, vibrato, fade... Your program could convert common note names to/from pitch.
Thank you guys so much for taking the time, it's been immensely helpful. I hadn't even considered a map being an option, and I do think that it solves the problem in the best way.

I'm still an undergrad comp-sci major, and so I haven't worked on any large projects yet. I got excited about the idea of doing this music composition/analysis program and decided it was time to throw myself in the deep end. While I'll likely be the only one to ever use this program, it's still really important to me to organize it as if it's a massive program built by a massive team, whose code I can show off and explain in detail 'why' the decisions I made were justifiable. My point in all of that is just to say again, thanks for taking the time as I do feel a lot more confident in how this is shaping up.

One last thing though; I'm assuming since no one really mentioned me organizing constants and certain functions into a single header that defines a namespace, that this is an acceptable programming practice?
No you wouldn't usually have a namespace of all constants. However if they are all related to an existing namespace then by all means put them in that namespace's header.
Topic archived. No new replies allowed.