Skip to content

Instantly share code, notes, and snippets.

@mynameisflorian
Last active August 20, 2022 17:34
Show Gist options
  • Save mynameisflorian/63d6348b78956215001baee4f2708ba7 to your computer and use it in GitHub Desktop.
Save mynameisflorian/63d6348b78956215001baee4f2708ba7 to your computer and use it in GitHub Desktop.
// 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