Last active
August 20, 2022 17:34
-
-
Save mynameisflorian/63d6348b78956215001baee4f2708ba7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Serializable enum type generator for c++ | |
// - low memory footprint(*) | |
// - Exception-free(*) | |
// - optimized for minimum memory footprint when not serializing(*) | |
// - supports using a single byte as the underlying data type(*) | |
// (or the default data type -- usually a 32 bit integer) | |
// (*) designed with low-memory microcontrollers as a primary target platform | |
// Overview | |
// ‾‾‾‾‾‾‾‾ | |
// A serializable enum is defined by creating a macro that follows the format of | |
// a regular enum, adapted as a multi-line x-macro function: | |
// #define name(wrapper){\ | |
// wrapper(enumerator1),\ | |
// wrapper(enumerator2),\ | |
// ... | |
// wrapper(enumeratorN)\ | |
// }; | |
// | |
// This is then passed to either CREATE_ENUM or CREATE_BYTE_ENUM which generates the actual enum | |
// and the internal template functions that are responsible for implementing the reflection | |
// constructs that allow that serialization and parsing functions to do the rest of the work. | |
// | |
// Seriaization is provided by an overloaded "to_string" function and parsing is proided by a | |
// "from_string_to<T>" function, where T is the ENUM type. The "to_string" function uses automatic | |
// type deduction to call the correct "to_string" function. "from_string_to<T>" requires the ENUM | |
// to be specified explicitly. See the examples at the end of this document for more details. | |
// _______________________________ | |
// [ Section: Implementation Notes ] | |
// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | |
// note: In ths document "ENUM" refers to the serializable enum type, | |
// whereas "enum" refers to the built-in type or general concept | |
// Code generation & execution flow: | |
// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | |
// ENUM "name" is defined and passed to CREATE_ENUM or CREATE_BYTE_ENUM | |
// "CREATE_ENUM name" uses the IDENTITY macro to generate the native enum code and envokes the other | |
// generator macros, roughly following this path | |
// --> Generator macros (CREATE_ENUM passes down the generated enum type via the "name" paramater) | |
// --> generates the reflection "getters" ("name" paramater becomes the type (<T>) templated to these functions at | |
// this step. Template specialization is used as a class/category to group the | |
// generated getters functions so they can be selected by the generic | |
// "to_string<T>" and "from_string_to<T>" functions via the deduced or supplied | |
// template type (<T>) so they can access the data vectors for any ENUM type | |
// supplied to the to/from-string functions | |
// --> pair of aligned vectors (used to serialize/parse enum values in to_string<T> and from_string_to<T>) | |
// _______________________ | |
// [ Section: Library Code ] | |
// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | |
#include <iostream> | |
#include <vector> | |
using namespace std; | |
// Abstracted generator macros | |
// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | |
#define IDENTITY(e) e | |
#define STRING(e) #e | |
// Undefined base templates for reflection getters | |
// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | |
template <typename T> auto get_enum_strings(); | |
template <typename T> auto get_enum_all(); | |
// Reflection Getter generator macros | |
// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | |
#define GET_ENUM_STRINGS(name) template <> auto get_enum_strings<name>(){ return vector<const char*> name(STRING) } | |
#define GET_ENUM_ALL(name) template <> auto get_enum_all<name>(){ return vector<name> name(IDENTITY) } | |
// Enum generator macros | |
// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | |
#define CREATE_ENUM(name) enum name name(IDENTITY) GET_ENUM_STRINGS(name) GET_ENUM_ALL(name) | |
#define CREATE_BYTE_ENUM(name) enum name : uint8_t name(IDENTITY) GET_ENUM_STRINGS(name) GET_ENUM_ALL(name) | |
// @FUNCTION: to_string(ENUM) | |
// @RETURNS: c-string (const char*) | |
// :: serializes enums generated by CREATE_ENUM and CREATE_BYTE_ENUM | |
template <typename T> | |
const char* to_string(T val){ | |
return get_enum_strings<T>()[val]; | |
} | |
// @FUNCTION: to_string(ENUM*) | |
// @RETURNS: c-string (const char*) | |
// :: serializes enums generated by CREATE_ENUM and CREATE_BYTE_ENUM | |
// :: returns an empty string if ENUM* is NULL | |
template <typename T> | |
const char* to_string(T *val){ | |
if(val==NULL) return ""; | |
return get_enum_strings<T>()[*val]; | |
} | |
// @FUNCTION: from_string_to<ENUM>(string) | |
// @RETURNS: ENUM | |
template <typename T> | |
T* from_string_to(string str){ | |
auto strings = get_enum_strings<T>(); | |
for( T v : get_enum_all<T>() ) | |
if(str.compare(strings[v])==0) return new T{v}; | |
return NULL; | |
} | |
// _______________________ | |
// [ Section: Example Code ] | |
// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | |
#define colors(enum){\ | |
enum(red),\ | |
enum(green),\ | |
enum(blue),\ | |
}; | |
CREATE_BYTE_ENUM(colors); | |
int main() { | |
// Regular use-case | |
std::cout << to_string(red) << endl; | |
// --> "red" | |
// Since it's an overloaded standard function, out of bounds integers are handled as integers | |
std::cout << to_string(5) << endl; | |
// --> "5" | |
// Actually, all integers are handled as integers | |
std::cout << to_string(1) << endl; | |
// --> "1" | |
// Both functions gracefully handle bad values | |
std::cout << to_string(from_string_to<colors>("gren")) << endl; | |
// --> to_string(NULL) --> "" | |
// Round-trip | |
std::cout << to_string(from_string_to<colors>("blue")) << endl; | |
// --> "blue" | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment