Page Image
String Value Object Pattern
Sometimes you've got to convert between an enumeration and a string...
Modified:
Created:

Often times in an application, you’ve got to convert between an enumeration value and a string. It’s generally best to wrap this in an object, so you keep all the details of the conversion contained in one place. It also makes the syntax of comparing objects a simple process of a == b or similar.

You’d probably want to use the Macro List Pattern to capture the details for each string and value pair. This is a good way to also associate additional metadata with an item (e.g. a human readable string, a uuid, etc)

Here’s an example:

c++
// myvalue.h
#pragma once

// Use the Macro List Pattern to capture the specifics for the values.
#define MY_VALUE_LIST \
	X(Invalid, "Invalid", 0) \
	X(ThingOne, "Thing One", 1) \
	X(ThingTwo, "Thing Two", 2)


class MyValue {
	#define X(a, b, c) a = c,
	public:
		enum ID {
			MY_VALUE_LIST
		};
	#undef X

		static ID stringToID(const char *string);
		static const char *stringFromID(ID val);

		MyValue(ID val = Invalid) : _id(val) {}
		MyValue(const char *val) : _id(stringToID(val)) {}
		MyValue(const std::string &str) : _id(stringToID(str.c_str())) {}

		operator==(const MyValue &o) const { return _id == o._id; }
		operator==(ID v) const { return v == _id; }
		operator!=(const MyValue &o) const { return _id != o._id; }
		operator!=(ID v) const { return v != _id; }

		bool isValid() const { return _id != Invalid; }
		ID id() const { return _id; }
		const char *toString() const { return stringFromID(_id); }

	private:
		ID _id;
}


// myvalue.cpp
#include "myvalue.h"

#define X(a, b, c) if(!strncmp(string, b, sizeof(b))) return a;
MyValue::ID MyValue::stringToID(const char *string) {
	MY_VALUE_LIST
	return Invalid;
}
#undef X

#define X(a, b, c) case a: ret = b; break;
const char *MyValue::stringFromID(ID val) {
	const char *ret = nullptr; // or perhaps "Invalid", depends on the implementation needs
	switch(val) {
		MY_VALUE_LIST
	}
	return ret;
}
#undef X

Please note the stringToID() uses a linear search, which are fine for small lists but may be unacceptably slow for larger lists. The could be implemented as a hash lookup from a hash that’s filled at startup by the appropriate X() macro.

Once you’ve got a object for your value, you can then use it as if you were using a regular enum or integer, but it’ll handle the translation to and from a string for you.

Example:

c++
MyValue a("Thing One");        // This will cause the string to integer lookup (slow)
MyValue b(MyValue::ThingOne);  // This will cause the value to be assigned to the ID (fast)

a == b; // This will evaluate true and is fast, since it's just an integer compare