Menu

Home

Georg Ulbrich

Welcome to cppviz!

Ever wished, you had C enums as similar powerful as Java enums? cppviz aims in this direction.

For example, write in your C code

#include "cppviz.h"

#define PLANET_DATA(_,__,___, T, A, E) \
    T(_,__,___, int         , char *        , int    ) \
    A(_,__,___, id          , name          , radius ) \
    /*----------------------------------------------*/ \
    E(_,__,___, MERCURY     , "Mercury"     , 2439   ) \
    E(_,__,___, VENUS       , "Venus"       , 6051   ) \
    E(_,__,___, EARTH       , "Earth"       , 6378   )

ENUM(PLANET);

Then the ENUM macro at the end lets the preprocessor create the following source

typedef enum _e_PLANET {
    PLANET_MERCURY,
    PLANET_VENUS,
    PLANET_EARTH,
} PLANET;

How many planets are there in the enum? Find out with

COUNT(PLANET);

which gives

(0 + 1 + 1 + 1);

which is ... ehm ... kind of right :-) Well, if you can count it, you can iterate through it!

FOREACH(PLANET, planet) {
    printf("%d\n", (int) planet);
}

From this, the preprocessor will create

for (PLANET planet = (PLANET) 0; planet < (0 + 1 + 1 + 1); planet = (PLANET) (((int) planet) + 1)) {
    printf("%d\n", (int) planet);
}

printing each planets ordinal. Nice, but nicer would be to have the attributes of each planet at hand, right?
To grab display name and radius, just add

ATTRIBUTE(PLANET, 2); /* for the "name" attribute */
ATTRIBUTE(PLANET, 3); /* for the "radius" attribute */

resulting in two arrays

char * PLANET_name[] = {
    "Mercury",
    "Venus",
    "Earth",
};

int PLANET_radius[] = {
    2439,
    6051,
    6378,
};

Want to have the ENUM NAMES of each of the planets, too? Get them with

NAMES(PLANET);

and this will create the array

const char * PLANET_element_name[] = {
    "MERCURY",
    "VENUS",
    "EARTH",
};

Finally, you can apply a planet-specific landing procedure with

SWITCH(PLANET, planet, landing_procedure(x, y));

which will turn into

switch (planet) {
case PLANET_MERCURY:
    PLANET_MERCURY_landing_procedure(x, y);
    break;
case PLANET_VENUS:
    PLANET_VENUS_landing_procedure(x, y);
    break;
case PLANET_EARTH:
    PLANET_EARTH_landing_procedure(x, y);
    break;
default:
    break;
};

Well, the specific landing functions/macros you will have to write for yourself :-)

Finally, a translation for a complete example. First, the source code

/* Include the magick. */
#include "cppviz.h"

/* and some other stuff */
#include <stdio.h>

/*
Create the data table for planets. The table name has to end
with "_DATA", everything before "_DATA" will be the main name,
here "PLANET". Sorry for the crude underscore parameters ...
*/
#define PLANET_DATA(_,__,___, T, A, E) \
    T(_,__,___, int         , char *        , int    ) \
    A(_,__,___, id          , name          , radius ) \
    /*----------------------------------------------*/ \
    E(_,__,___, MERCURY     , "Mercury"     , 2439   ) \
    E(_,__,___, VENUS       , "Venus"       , 6051   ) \
    E(_,__,___, EARTH       , "Earth"       , 6378   )

/* Now, create the enum, simple as that! */
ENUM(PLANET);

/* We want to access the planets attributes, name and radius. Simple as that! */
ATTRIBUTE(PLANET, 2);
ATTRIBUTE(PLANET, 3);

/* We also want the enum ELEMENT NAMES of all planets, simple as that! */
NAMES(PLANET);

/* The landing procedures, one for each planet. Your job to fill them ... */
void PLANET_MERCURY_landing_procedure(int x, int y) { /* ... */ }
void PLANET_VENUS_landing_procedure(int x, int y) { /* ... */ }
void PLANET_EARTH_landing_procedure(int x, int y) { /* ... */ }

/* Let the spaceship roll! */
int main() {

    /* Some totally relevant x, y coordinates. */
    int x, y;

    /* Print stuff for each planet, simple as that! */
    FOREACH(PLANET, planet) {
        printf("%s %s %d\n", PLANET_element_name[planet], PLANET_name[planet], PLANET_radius[planet]);
    }

    /* Perform the planet specific landing procedure. */
    /* Yes, simple as that! */
    PLANET planet;
    /* ... */
    SWITCH(PLANET, planet, landing_procedure(x, y));

}

and the result is

typedef enum _e_PLANET { PLANET_MERCURY, PLANET_VENUS, PLANET_EARTH, } PLANET;
char * PLANET_name[] = { "Mercury", "Venus", "Earth", };
int PLANET_radius[] = { 2439, 6051, 6378, };
const char * PLANET_element_name[] = { "MERCURY", "VENUS", "EARTH", };

void PLANET_MERCURY_landing_procedure(int x, int y) { }
void PLANET_VENUS_landing_procedure(int x, int y) { }
void PLANET_EARTH_landing_procedure(int x, int y) { }

int main() {
    int x, y;

    for (PLANET planet = (PLANET) 0; (int) planet < (0 + 1 + 1 + 1); planet = (PLANET) (((int) planet) + 1)) {
        printf("%s %s %d\n", PLANET_element_name[planet], PLANET_name[planet], PLANET_radius[planet]);
    }

    PLANET planet;

    switch (planet) {
    case PLANET_MERCURY:
        PLANET_MERCURY_landing_procedure(x, y);
        break;
    case PLANET_VENUS:
        PLANET_VENUS_landing_procedure(x, y);
        break;
    case PLANET_EARTH:
        PLANET_EARTH_landing_procedure(x, y);
        break;
    default:
        break;
    };

}

Technical details

You guessed it, some conventions are called for.

  1. The naming scheme is that the name of the data table consists of the object name (PLANET) followed by "_DATA".
  2. The signature (_,__,___, T, A, E) of the data table macro is mandatory. The underscore parameters are a technical necessity.
  3. The T-row in the data table describes the types of the attributes.
  4. The A-row describes the names of the attributes.
  5. The E-rows describe the elements of the enum.
  6. When using the ATTRIBUTE macro, the attribute index is 1-based and starts with the column of enum ids (MERCURY, VENUS, EARTH).
  7. At the moment, a data table can have up to 100 attributes, but if need be, you can increase this number by adjusting the line "n=100" in cppviz.h and running the Makefile.

Thats about it, have fun!
Georg

GCC-tested!


Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.