How can I bundle additional information to an enum? - c++

Let's say I have an enumeration of statuses for a game I'm working on. At first, I have something like this:
enum class Status {
OK, HURT, DYING, DEAD
};
That's all fine and dandy, but now I want to print the name of the status. Naturally, if this was a normal class, I would call getName() on an instance of Status. However, no such option exists for an enumeration.
A way to solve this problem is to have something like this:
const char * getName(Status status) {
switch(status) {
case Status::OK:
return "OK";
break;
/*and so on */
}
However, this is clearly not very extensible. It becomes a problem when you have a lot of enums with lots of data tied to them. You also don't really tie related data together in a very meaningful way. With uniform call syntax, you could get something close, like this:
Status::OK.getName();
But that's not standard yet.
Another way to solve this problem is through static const members inside a class, like so:
class Status {
std::string name;
Status(std::string name):name(std::move(name)) {}
public:
const std::string & getName() const { return name; }
static const Status OK, HURT, DYING, DEAD;
};
//in some .cpp
const Status Status::OK("OK"), Status::HURT("Hurt"), Status::DYING("Dying"), Status::DEAD("Dead");
That's all well and good, and it works out fine, for a while. But now, I want to write some code to deal with each Status, so instinctively I prefer a switch over an if-else chain.
I write this:
Status status = getStatus();
switch(status) {
case Status::OK:
//do something
default:
//do something else
}
Of course, this doesn't work. I need a converting operator! So I add that
operator int() { return /*some sort of unique value*/; }
And... still nothing. The reasoning is that the operator must be a constexpr. Which is obvious, knowing the detail of switch, but it doesn't really help. So the next logical step is to have the static consts become static constexprs.
class Status {
const char * name; //we are in constexpr land now
constexpr Status(const char * name):name(name) {}
public:
constexpr const char * getName() const { return name; }
constexpr operator int() const { return /*some sort of unique value*/; }
static constexpr Status OK = Status("OK"),
HURT = Status("Hurt"),
DYING = Status("Dying"),
DEAD = Status("Dead");
};
//in some .cpp
constexpr Status Status::OK, Status::HURT, Status::DYING, Status::DEAD;
The reason for having to put the statics is because constexpr have to be initialized on the spot. This would work fine, and honestly it covers all my needs. The only problem is that it doesn't compile. It makes sense: how can a class whose definition is not done yet be initialized? This is not a problem with the consts, because they are delayed in their instantiation.
So now, the only reasonable way to do something like that is through a another class, so that the size of Status is known when we create the constexprs.
class Status {
const char * name;
constexpr Status(const char * name):name(name) {}
public:
constexpr const char * getName() const { return name; }
constexpr operator int() const { return /*some sort of unique value*/; }
struct constants;
};
struct Status::constants {
static constexpr Status OK = Status("OK"),
HURT = Status("Hurt"),
DYING = Status("Dying"),
DEAD = Status("Dead");
};
//in some .cpp
constexpr Status Status::constants::OK, Status::constants::HURT, Status::constants::DYING, Status::constants::DEAD;
It's a distance away from the natural syntax of Status::NAME, instead having to say something like Status::constants::NAME. Another concern is the operator int(). There is no way (that I know of) to make this be populated in a way such as an enum would be. In a static const implementation, the obvious choice is to have a private static int, increment it in the ctor, and store it, using that as the return value of the conversion operator. However, this is no longer an option in the constexpr version. Maybe there's some sort of template magic to do it, and if so I'd love to see it. So my question is, are there any improvements to be done using modern C++ to the way we bundle information to an enum?

Based on Columbo's answer here: Is a class definition complete when it is passed to a base class?, it is possible to achieve expanded enums.
template<class T>
class Constants {
public:
static const T OK, HURT, DYING, DEAD;
};
template <typename T>
constexpr T Constants<T>::OK = T("OK");
template <typename T>
constexpr T Constants<T>::HURT = T("Hurt");
template <typename T>
constexpr T Constants<T>::DYING = T("Dying");
template <typename T>
constexpr T Constants<T>::DEAD = T("Dead");
class Status : public Constants<Status> {
friend class Constants<Status>;
const char * name;
constexpr Status(const char * name):name(name) {}
public:
constexpr const char * getName() const { return name; }
constexpr operator int() const { return /*some sort of unique value*/; }
};
While there are still some issues with this implementation, such as a lack of returning a unique value (probably have to just make the ctor take one and do it manually), it looks pretty good. Note however this won't compile on MSVC (due to lack of good constexpr support) or Clang (due to bug 24541). But hey, it's the best we've got so far.

Related

Call an "initialize" function in an object when one of its member functions are called

I am trying to implement lazy initializing in C++ and I am searching for a nice way to call the Initialize() member function when some other method like object->GetName() gets called.
Right now I have implemented it as follows:
class Person
{
protected:
bool initialized = false;
std::string name;
void Initialize()
{
name = "My name!"; // do heavy reading from database
initialized = true;
}
public:
std::string GetName()
{
if (!initialized) {
Initialize();
}
return name;
}
};
This does exactly what I need for the time being. But it is very tedious to setup the initialized check for every method, so I want to get rid of that. If someone knows a nice way in C++ to improve this above example, I would like to know!
Could maybe operators be used to achieve calling Initialize() when using -> for example?
Thanks!
Sounds like a job for templates! Create a lazily_initialized wrapper that takes a type T and a function object TInitializer type:
template <typename T, typename TInitializer>
class lazily_initialized : TInitializer
{// ^^^^^^^^^^^^^^
// inheritance used for empty-base optimization
private:
T _data;
bool _initialized = false;
public:
lazily_initialized(TInitializer init = {})
: TInitializer(std::move(init))
{
}
T& get()
{
if(!_initialized)
{
static_cast<TInitializer&>(*this)(_data);
_initialized = true;
}
return _data;
}
};
You can the use it as follows:
struct ReadFromDatabase
{
void operator()(std::string& target) const
{
std::cout << "initializing...\n";
target = "hello!";
}
};
struct Foo
{
lazily_initialized<std::string, ReadFromDatabase> _str;
};
Example:
int main()
{
Foo foo;
foo._str.get(); // prints "initializing...", returns "hello!"
foo._str.get(); // returns "hello!"
}
example on wandbox
As Jarod42 mentioned in the comments, std::optional<T> or boost::optional<T> should be used instead of a separate bool field in order to represent the "uninitialized state". This allows non default-constructible types to be used with lazily_initialized, and also makes the code more elegant and safer.
As the former requires C++17 and the latter requires boost, I used a separate bool field to make my answer as simple as possible. A real implementation should consider using optional, using noexcept where appropriate, and also consider exposing a const-qualified get() that returns a const T&.
Maybe call it in the constructor?
Edit: Uh, i missed the point of your question sorry.
What about a lazy factory initialization?
https://en.wikipedia.org/wiki/Lazy_initialization#C.2B.2B

Function template parameter

I ran into the following code that defines a function template in a class:
#include <cstdint>
class foo {
public:
enum class magic_type : std::uint32_t {
START = 0,
BLUE = 0xFF000001,
RED,
};
struct header_t {
uint32_t version;
magic_type magic;
};
template <typename T>
static bool is_of_type(header_t *h)
{
return (h->magic == T::magic_type);
}
foo(uint32_t ver, foo::magic_type mag)
{
header.version = ver;
header.magic = mag;
}
header_t header;
};
I am finding the implementation of 'is_of_type` confusing. The code as is compiles, so syntactically must be correct. However, this method is not invoked from any other part of the program, so I am not sure what the intent of the function is (lack of documentation). I figured there could be two interpretation of the function:
Return true/false based on the magic type of an object and the specific enum type passed as the function template parameter.
E.g. An invocation of the method would be:
foo bar(1.2, foo::magic_type::BLUE);
bool temp = bar.is_of_type<foo::magic_type::BLUE>(&(bar.header));
However, in the above case, I am not really passing a type (as in an int, or char, etc). Right? The code does not compile.
Return true/false if the magic type is a valid enum.
In this case, I am assuming the function does not need to be templated, and could be re-written as:
static bool is_of_type(header_t *h)
{
return (h->magic == foo::magic_type);
}
E.g. of an invocation:
foo bar(1.2, foo::magic_type::BLUE);
bool temp = bar.is_of_type(&(bar.header));
Again, getting compile error. I tried using "typename", but my attempts were futile.
Can someone please help me with proper implementation of is_of_type for the above two cases and an invocation example.
The invocation would be with an explicitly specified type, which has a nested static member called magic_type.
For instance, it could be called as follows:
struct test {
static foo::magic_type const magic_type;
};
foo::magic_type const test::magic_type = 42;
foo bar{1, foo::magic_type::BLUE};
bar.is_of_type<test>(bar.header);
The fact that magic_type is used twice, once for an enum class and once for a static variable, is very confusing though.

C++ strategy for storing global std::string label with getter and setter

In a C++ project I'm working on, I need to keep track of a label. The label simply stores a std::string that is appended to results written to various files so that the results can be mapped to a particular phase of the implemented algorithms.
Here are the requirements of the mechanism for keeping track of the label:
All translation units need access to this label
Label must be able to be modified during runtime
Need to control access to the label via getter/setter functions
Always need exactly 1 label
This is not hard to implement. But, nonetheless, I'm raising the question here because I suspect this is something commonly done --- or, at least, very similar to things commonly done.
The best solution I can think of is to have a class such as the following, and then just include the interface everywhere:
class Label {
public:
static std::string get();
static int set(std::string s);
private:
static std::string label;
};
std::string Label::get() { return label; }
int Label::set(std::string s) {
if( /* OK to change to "s" */ ) {
label = s;
return 0;
}
return 1;
}
std::string Label::label = "";
Because there's always exactly 1 of these labels, it seems like there ought to be a better solution than creating a class. Any suggestions?
I'd tend to wonder whether there might not be more use for a broader class, something like:
template <class T>
class cond_write {
T val;
std::function<bool()> c;
public:
template <class Cond>
cond_write(T const &t, Cond c): val(t), c(c) {}
cond_write &operator=(T const &t) {
if (c())
val=t;
return *this;
}
operator T() const { return val; }
};
Then you'd instantiate it with (in your case) std::string, and a lambda for the condition under which a write can happen.
Instead of get and set, you'd just assign to it, or use it as a T (std::string, in your case). For example:
cond_write<std::string> label("Initial label", []() { return whatever(); });
// equivalent to label.set("This might be the new label");
label="This might be the new label";
// equivalent to std::string new_label = label.get();
std::string new_label=label;
I see no need for a class here and recommend using free functions and a namespace. You have the same scoping semantics but without all of the decorations such as static. They also allow you to keep internals private just as you would with a class. A few minor changes and your code ends up like the following.
Header
namespace Label
{
std::string get();
// only require a copy when necessary and allow rvalues.
int set(const std::string& s);
};
Implementation
namespace // unnamed namespace is generally recommended over using static
{
std::string label;
}
std::string Label::get() { return label; }
int Label::set(const std::string& s)
{
if( /* OK to change to "s" */ )
{
label = s;
return 0;
}
return 1;
}

.(dot) access to static method

Following is a popular code:
class A
{
public:
static const string TYPE = "AEvent";
const string& getType() { return TYPE; }
};
Above code could be use like this:
if (aInstance.getType() == A::TYPE)
{
...
}
It's fine. But it's not intuitiveness. Did not? Look at the next code:
class A
{
public:
static const string& getType()
{
static const string TYPE = "AEvent";
return TYPE;
}
}
//Usage
if (aInstance.getType() == A::getType())
....
Of course, getType is static method but it's possible to access dot operator and It's more intuitively see to me.
How do you think about?
If it's clearer to use obj.static_member (or method), then use it; this is often true when the type name is long, such as with templates. Otherwise use Type::static_member.
As long as you're returning a static variable from static method it should be fine. It doesn't matter if you call it using dot or resolution operator. Either way it's part of the object as well as the class which should give you the same static variable.

How not to compile casting into enum type in C++?

I have this "better" enum class that
cannot contain invalid values, and
cannot be used until enum value is not set explicitly,
as follows:
class Symmetry
{
public:
enum Type {
GENERAL, SYMMETRIC, HERMITIAN,
SKEW_SYMMETRIC, SKEW_HERMITIAN, UNINITIALIZED
};
Symmetry() { t_ = UNINITIALIZED }
explicit Symmetry(Type t) : t_(t) { checkArg(t); }
Symmetry& operator=(Type t) { checkArg(t); t_ = t; return *this; }
operator Type() const {
if (t_ == UNINITIALIZED) throw runtime_error("error");
return t_;
}
private:
Type t_;
void checkArg(Type t) {
if ((unsigned)t >= (unsigned)UNINITIALIZED)
throw runtime_error("error");
}
};
This allows me to write the following code:
Symmetry s1(Symmetry::SYMMETRIC);
Symmetry s2;
s2 = Symmetry::HERMITIAN;
Symmetry s3;
if (Symmetry::GENERAL == s3) // throws
My problem is that a compiler allows constructs such as:
Symmetry s1((Symmetry::Type)18); // throws
Symmetry s2;
s2 = (Symmetry::Type)18; // throws
I solved this problem by throwing exceptions, but I would prefer such a code not to compile at all (a compile time error). Is there a way how to manage this?
Potentially a crummy solution, but it would solve your immediate problem. Rather than having an inner enum type, define a little helper class with a private constructor, and make the outer class a friend. Then the "enum" values can be static const members in your outer class. Something like this:
(DISCLAIMER: untested, so there may be various compilation issues, but you should get the idea)
class Symmetry
{
public:
class Type
{
private:
Type() {};
friend class Symmetry;
};
static const Type GENERAL;
static const Type SYMMETRIC;
static const Type HERMITIAN;
};
You will need some way of determining equality, but this should be fairly easy.
My attempt using templates: (tested. However, this can be further improved!)
template<int N>
struct Symmetry
{
enum Type
{
GENERAL, SYMMETRIC, HERMITIAN,
SKEW_SYMMETRIC, SKEW_HERMITIAN
};
template<Type e> struct allowed;
template<> struct allowed<GENERAL> { static const int value = GENERAL; };
template<> struct allowed<SYMMETRIC> { static const int value = SYMMETRIC; };
template<> struct allowed<HERMITIAN> { static const int value = HERMITIAN; };
template<> struct allowed<SKEW_SYMMETRIC> { static const int value = SKEW_SYMMETRIC; };
template<> struct allowed<SKEW_HERMITIAN> { static const int value = SKEW_HERMITIAN; };
allowed<(Type)N> m_allowed;
operator int()
{
return N;
}
};
Symmetry<0> e0; //okay
Symmetry<1> e1; //okay
Symmetry<100> e4; //compilation error!
Symmetry<e0.SKEW_HERMITIAN> e3; //okay
Symmetry<e0.SKEW_SYMMETRIC> e3; //okay
Usage:
int main()
{
Symmetry<0> e0;
Symmetry<e0.HERMITIAN> e1;
switch (e1)
{
case e0.HERMITIAN:
{
cout << "It's working" << endl;
}
break;
}
}
No. If you allow any cast to be used, as your last example does, then there will always be some cast that can be used to subvert your type.
The solution is to not be in the habit of using these casts and to very suspiciously consider any code that uses these casts indiscriminately. View this type of casting as the nuclear bomb in your arsenal: it's important to have, but you always handle it with care and never want to deploy it more than rarely.
What warning options does your compiler have for casting? What lint tools are you using which may detect this misuse of casts?
That said, it appears you really want to hide the inner Type so users are less tempted to even use it. Realizing that, it's straight-forward to make that type name private, even while not preventing all cast misuse, by slightly tweaking your original:
struct Symmetry {
enum {
UNINITIALIZED,
GENERAL, SYMMETRIC, HERMITIAN,
SKEW_SYMMETRIC, SKEW_HERMITIAN
};
private:
typedef decltype(UNINITIALIZED) Hidden;
Hidden _value;
public:
Symmetry(Hidden value = UNINITIALIZED) : _value (value) {}
Symmetry& operator=(Hidden value) { _value = value; return *this; }
operator Hidden() const {
if (_value == UNINITIALIZED) {
throw std::logic_error("uninitialized Symmetry");
}
return _value;
}
bool initialized() const { return _value != UNINITIALIZED; }
// required if you want to check for UNINITIALIZED without throwing in
// the above conversion
};
This is a complete implementation, no details omitted or unknown, or issues with initialization order. The only caveat is decltype – with a pre-C++0x compiler, you'll have to use something implementation-specific or a library which wraps something implementation-specific.
And a smaller issue: change from runtime_error to logic_error, as using uninitialized values should be preventable beforehand and thus falls in the latter category.