X macros: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
(→See also: correct order) |
(serialisation) |
||
Line 1: | Line 1: | ||
__TOC__ |
|||
== Introduction == |
|||
Definitions: |
Definitions: |
||
Line 88: | Line 92: | ||
300 => ADD |
300 => ADD |
||
301 => SUB |
301 => SUB |
||
</pre> |
|||
== Serialization == |
|||
You can use the same technique to define structs and also generate code to serialize them: |
|||
<source lang="C++"> |
|||
#include <stdio.h> |
|||
#include <string.h> |
|||
typedef char *char_ptr; |
|||
void serialize_char_ptr(const char *x) |
|||
{ |
|||
printf("\"%s\"\n", x); |
|||
} |
|||
void serialize_int(int x) |
|||
{ |
|||
printf("%d\n", x); |
|||
} |
|||
#define _DEFINE_STRUCT_MEMBER(type, name) type name; |
|||
#define _DEFINE_STRUCT_SERIALIZE(type, name) serialize_##type(x->name); |
|||
#define DEFINE_STRUCT(name) \ |
|||
struct name { \ |
|||
name(_DEFINE_STRUCT_MEMBER) \ |
|||
}; \ |
|||
\ |
|||
void serialize_##name(struct name *x) \ |
|||
{ \ |
|||
name(_DEFINE_STRUCT_SERIALIZE) \ |
|||
} |
|||
#define player(X) \ |
|||
X(char_ptr, name) \ |
|||
X(int, age) |
|||
DEFINE_STRUCT(player); |
|||
int main(int argc, char *argv[]) |
|||
{ |
|||
player vegard = { |
|||
.name = strdup("Vegard"), |
|||
.age = 32, |
|||
}; |
|||
serialize_player(&vegard); |
|||
return 0; |
|||
} |
|||
</source> |
|||
Output: |
|||
<pre> |
|||
"Vegard" |
|||
32 |
|||
</pre> |
</pre> |
||
Revision as of 06:46, 15 December 2019
Introduction
Definitions:
#define _DEFINE_ENUM_NAME(name) name,
#define _DEFINE_ENUM_STR(name) #name,
#define DEFINE_ENUM(name) \
enum name { \
name(_DEFINE_ENUM_NAME) \
}; \
\
const char *name##_names[] = { \
name(_DEFINE_ENUM_STR) \
}; \
\
const unsigned int nr_##name##s = sizeof(name##_names) / sizeof(*name##_names);
Usage:
#define opcode(X) \
X(LOAD_CONSTANT) \
X(LOAD_LOCAL) \
X(STORE_LOCAL) \
X(ADD) \
X(SUB)
DEFINE_ENUM(opcode)
This defines enum opcode which contains LOAD_CONSTANT, etc. You can also map enum values to strings using the opcode_names array, like this:
void print_value(enum opcode opc)
{
printf("%s\n", opcode_names[opc]);
}
Discontiguous values
For bitfields and cases where you need specific values, you can also modify the definition to allow specific/discontiguous enum values.
To avoid the name array from growing too large it is probably useful to use a map instead of an array.
#include <map>
#define _DEFINE_ENUM_NAME(name) name,
#define _DEFINE_ENUM_NAME_VALUE(name, value) name = value,
#define _DEFINE_ENUM_VALUE_STR1(name) { name, #name },
#define _DEFINE_ENUM_VALUE_STR2(name, value) { name, #name },
#define DEFINE_ENUM(name) \
enum name { \
name(_DEFINE_ENUM_NAME, _DEFINE_ENUM_NAME_VALUE) \
}; \
\
std::map<enum name, const char *> name##s = { \
name(_DEFINE_ENUM_VALUE_STR1, _DEFINE_ENUM_VALUE_STR2) \
};
#define opcode(X, Y) \
Y(LOAD_CONSTANT, 100) \
X(LOAD_LOCAL) \
Y(STORE_LOCAL, 200) \
Y(ADD, 300) \
X(SUB)
DEFINE_ENUM(opcode)
int main(int argc, char *argv[])
{
for (auto &it: opcodes)
printf("%d => %s\n", it.first, it.second);
return 0;
}
This prints:
100 => LOAD_CONSTANT 101 => LOAD_LOCAL 200 => STORE_LOCAL 300 => ADD 301 => SUB
Serialization
You can use the same technique to define structs and also generate code to serialize them:
#include <stdio.h>
#include <string.h>
typedef char *char_ptr;
void serialize_char_ptr(const char *x)
{
printf("\"%s\"\n", x);
}
void serialize_int(int x)
{
printf("%d\n", x);
}
#define _DEFINE_STRUCT_MEMBER(type, name) type name;
#define _DEFINE_STRUCT_SERIALIZE(type, name) serialize_##type(x->name);
#define DEFINE_STRUCT(name) \
struct name { \
name(_DEFINE_STRUCT_MEMBER) \
}; \
\
void serialize_##name(struct name *x) \
{ \
name(_DEFINE_STRUCT_SERIALIZE) \
}
#define player(X) \
X(char_ptr, name) \
X(int, age)
DEFINE_STRUCT(player);
int main(int argc, char *argv[])
{
player vegard = {
.name = strdup("Vegard"),
.age = 32,
};
serialize_player(&vegard);
return 0;
}
Output:
"Vegard" 32