4

In C++, to have a tidier code, I want to declare a set of values as constants in my header file, example :

constexpr float SIZE_SMALL = 1.5f; 
constexpr float SIZE_MEDIUM = 2.5f; 
constexpr std::string COLOR_RED = "RED"; 
constexpr std::string MATERIAL_SILK = "SILK";
...etc

But this is getting too long and clumsy. Also, some constants can be grouped together because they describe different values of the same property e.g. SIZE_SMALL and SIZE_MEDIUM.

What is the best way to write this in my header file? I thought about structs e.g.

struct SIZE
{
float SMALL; 
float MEDIUM; 
}

but then I have to declare and define a variable in my .cpp and that kinda beats the purpose of all this.

Fakher Mokadem
  • 1,059
  • 1
  • 8
  • 22
  • 1
    Could you explain _"But this is getting too long and clumsy"_ please? I'd d'ont necessarily see why. I'd group those constants by semantic, putting them in nested namespaces eventually `namespace size { constexpr float small = 1.5f; }`. – YSC Dec 02 '19 at 10:45
  • what about `static constexpr` member variable? –  Dec 02 '19 at 10:45
  • @YSC, I have some 50+ of these constants in the same name space – Fakher Mokadem Dec 02 '19 at 10:46
  • Well, namespaces are free you know. – YSC Dec 02 '19 at 10:46
  • oh, you mean a namespace inside a namespace? sounds good – Fakher Mokadem Dec 02 '19 at 10:48
  • you should declare all your namespace/global scope variables in your header inline. otherwise every translationunit gets its own copy of the variable and you may violate the one definition rule of other inline functions which use these variables. – phön Dec 02 '19 at 13:28

2 Answers2

6

then I have to declare and define a variable in my .cpp and that kinda beats the purpose of all this.

No you dont. You can make the fields static and constexpr :

struct SIZES {
    static constexpr const float SMALL = 1.5f;
};

However, I recommend to not do this. Not only you do not have to create an instance of this class, but also you should not create an instance of this class (what for?). So why use a class in the first place? Use namespaces instead to group constants:

namespace SIZES {
    constexpr float SMALL = 1.5f; 
}

I have some 50+ of these constants in the same name space

Unless you find a way to make the compiler read your mind, you have to write them all down somewhere. Serisously, the only way this could be condesned is if there is some relation between those constants, as eg SMALL + 1.0f = MEDIUM, but thats not apparent from your question.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • ok for the namespace (y). But if I define a struct -with the static and constexpr fields- without declaring an instance, how will I use that stuct in the code? – Fakher Mokadem Dec 02 '19 at 10:51
  • 1
    @FakherMokadem The point of `static` members is that you _don't_ need an instance to access them... – Max Langhof Dec 02 '19 at 10:52
  • 2
    @FakherMokadem `SIZES::SMALL`, btw I just suggeted you to **not** use static const members of a struct... – 463035818_is_not_an_ai Dec 02 '19 at 10:52
  • 1
    @formerlyknownas_463035818: I don't think that you need a `const` next to the `constexpr`. For variables, the `constexpr` is also a `const`. It is mentioned in Effective Modern C++, by Scott Meyers. – Constantinos Glynos Dec 02 '19 at 10:54
  • @ConstantinosGlynos I am not sure if it has any effect, still I like to have it there. At least it has an effect on someone not yet familiar with `constexpr`. How much this makes sense, depends on who will read your code (for me it does) – 463035818_is_not_an_ai Dec 02 '19 at 10:56
  • In C++17: `inline constexpr float SMALL = 1.5f;` – Evg Dec 02 '19 at 11:03
5

This depends on your actual usage but consider using proper strong types instead of basic types. For instance, to declare sizes, make them of type size, not float. This doesn’t directly solve your grouping problem but it gives you all of the other benefits of strong typing, and may also help with grouping:

struct size {
private: float value;
public:
    explicit constexpr size(float value) : value{value} {}
    explicit constexpr operator float() const noexcept { return value; }
};

namespace default_sizes {
    constexpr auto SMALL = size{1.5f};
    constexpr auto MEDIUM = size{2.5f};
    // …
}

In fact, for domain-specific usage, except in very limited cases, I’d generally avoid bare basic types and always encapsulate them in their own domain-specific type.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • is it possible to initiate an enum with anything other than ints? – Fakher Mokadem Dec 02 '19 at 11:05
  • 2
    @FakherMokadem Yes, [you can use non-`int`s as the underlying type](https://en.cppreference.com/w/cpp/language/enum), but they still need to be integral types. Note that I didn’t say “use an enum” (because you can’t), I suggested to “*imitate* an enum”. – Konrad Rudolph Dec 02 '19 at 11:06