Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I am programming a game of Tetris to be played in the terminal. Of course, it wouldn't be Tetris without fancy colors! Unfortunately, to print pretty colors, the code must look ugly. There are "ANSI escape sequences" I have used to create colors, and the title screen of my program looks like this. However, to create this beautiful screen, I want to make the code look as neat as possible.
So far I have devised two ways of condensing this code: definitions and strings. Here is the version of the code that uses definitions:
#define TEXT_ADMIN "\e[31m"
#define TEXT_ORANGE "\e[38;5;202m"
#define TEXT_BROWN "\e[38;5;130m"
#define TEXT_YELLOW "\e[93m"
#define TEXT_PUKE "\e[33m"
#define TEXT_LIME "\e[92m"
#define TEXT_GREEN "\e[32m"
#define TEXT_AQUA "\e[96m"
#define TEXT_SKY "\e[94m"
#define TEXT_MAGENTA "\e[35m"
#define COLOR_RESET "\e[0m"
#include <stdio.h>
int main()
{
char space[]=" ";
printf(
"%s"TEXT_ADMIN"████████╗"TEXT_ORANGE"███████"TEXT_BROWN"╗"TEXT_YELLOW"████████"TEXT_PUKE"╗"TEXT_LIME"██████"TEXT_GREEN"╗"TEXT_AQUA" ██"TEXT_SKY"╗"TEXT_MAGENTA"███████╗"COLOR_RESET"\n"
"%s"TEXT_ADMIN"╚══██╔══╝"TEXT_ORANGE"██"TEXT_BROWN"╔════╝"TEXT_PUKE"╚══"TEXT_YELLOW"██"TEXT_PUKE"╔══╝"TEXT_LIME"██"TEXT_GREEN"╔══"TEXT_LIME"██"TEXT_LIME"╗"TEXT_AQUA"██"TEXT_SKY"║"TEXT_MAGENTA"██╔════╝"COLOR_RESET"\n"
"%s"TEXT_ADMIN" ██║ "TEXT_ORANGE"█████"TEXT_BROWN"╗ "TEXT_YELLOW"██"TEXT_PUKE"║ "TEXT_LIME"██████"TEXT_GREEN"╔╝"TEXT_AQUA"██"TEXT_SKY"║"TEXT_MAGENTA"███████╗"COLOR_RESET"\n"
"%s"TEXT_ADMIN" ██║ "TEXT_ORANGE"██"TEXT_BROWN"╔══╝ "TEXT_YELLOW"██"TEXT_PUKE"║ "TEXT_LIME"██"TEXT_GREEN"╔══"TEXT_LIME"██"TEXT_GREEN"╗"TEXT_AQUA"██"TEXT_SKY"║"TEXT_MAGENTA"╚════██║"COLOR_RESET"\n"
"%s"TEXT_ADMIN" ██║ "TEXT_ORANGE"███████"TEXT_BROWN"╗ "TEXT_YELLOW"██"TEXT_PUKE"║ "TEXT_LIME"██"TEXT_GREEN"║ "TEXT_LIME"██"TEXT_GREEN"║"TEXT_AQUA"██"TEXT_SKY"║"TEXT_MAGENTA"███████║"COLOR_RESET"\n"
"%s"TEXT_ADMIN" ╚═╝ "TEXT_BROWN"╚══════╝ "TEXT_PUKE"╚═╝ "TEXT_GREEN"╚═╝ ╚═╝"TEXT_SKY"╚═╝"TEXT_MAGENTA"╚══════╝"COLOR_RESET"\n\n"
"%s PRESS ANY KEY TO BEGIN\n\n",
space,space,space,space,space,space,space);
}//TETRIS = 6 lines, text = 1 line, 8 lines total including space.
(An ugly monstrosity requiring much horizontal scrolling)
And here is the version which uses strings:
#include <stdio.h>
int main()
{
char admin[]="\e[31m";
char orange[]="\e[38;5;202m";
char brown[]="\e[38;5;130m";
char yellow[]="\e[93m";
char puke[]="\e[33m";
char lime[]="\e[92m";
char green[]="\e[32m";
char aqua[]="\e[96m";
char sky[]="\e[94m";
char magenta[]="\e[35m";
char reset[]="\e[0m";
char space[]=" ";
printf(
"%s%s████████╗%s███████%s╗%s████████%s╗%s██████%s╗%s ██%s╗%s███████╗%s\n"
"%s%s╚══██╔══╝%s██%s╔════╝%s╚══%s██%s╔══╝%s██%s╔══%s██%s╗%s██%s║%s██╔════╝%s\n"
"%s%s ██║ %s█████%s╗ %s██%s║ %s██████%s╔╝%s██%s║%s███████╗%s\n"
"%s%s ██║ %s██%s╔══╝ %s██%s║ %s██%s╔══%s██%s╗%s██%s║%s╚════██║%s\n"
"%s%s ██║ %s███████%s╗ %s██%s║ %s██%s║ %s██%s║%s██%s║%s███████║%s\n"
"%s%s ╚═╝ %s╚══════╝ %s╚═╝ %s╚═╝ ╚═╝%s╚═╝%s╚══════╝%s\n\n"
"%s PRESS ANY KEY TO BEGIN\n\n",
space,admin,orange,brown,yellow,puke,lime,green,sky,blue,magenta,reset,
space,admin,orange,brown,puke,yellow,puke,lime,green,lime,green,sky,blue,magenta,reset,
space,admin,orange,brown,yellow,puke,lime,green,sky,blue,magenta,reset,
space,admin,orange,brown,yellow,puke,lime,green,lime,green,sky,blue,magenta,reset,
space,admin,orange,brown,yellow,puke,lime,green,lime,green,sky,blue,magenta,reset,
space,admin,brown,puke,green,blue,magenta,reset,
space);
}//TETRIS = 6 lines, text = 1 line, 8 lines total including space.
As you can see, the string method looks somewhat cleaner, even though it uses more lines and memory, but even this method is quite long. Have I condensed this bit of code as much as I can? Is it even worth it to condense this? Thanks for your input!
I ended up with:
// colors.h
#define COLOR_ADMIN "\033[31m"
#define COLOR_ORANGE "\033[38;5;202m"
#define COLOR_BROWN "\033[38;5;130m"
#define COLOR_YELLOW "\033[93m"
#define COLOR_PUKE "\033[33m"
#define COLOR_LIME "\033[92m"
#define COLOR_GREEN "\033[32m"
#define COLOR_AQUA "\033[96m"
#define COLOR_SKY "\033[94m"
#define COLOR_MAGENTA "\033[35m"
#define COLOR_RESET "\033[0m"
// welcometext.c
#include "colors.h"
#define PRE " "
#define T COLOR_ADMIN
#define E COLOR_ORANGE
#define e COLOR_BROWN
#define T2 COLOR_YELLOW
#define t2 COLOR_PUKE
#define RC COLOR_GREEN
#define rc COLOR_AQUA
#define I COLOR_SKY
#define i COLOR_AQUA
#define S COLOR_MAGENTA
#define POST COLOR_RESET"\n"
const char welcomestring[] =
PRE T "████████╗" E "███████"e "╗" T2"███" "█████"t2"╗" RC"██████"rc"╗ " I"██"i"╗" S"███████╗" POST
PRE T "╚══██╔══╝" E "██"e "╔════╝" t2"╚══"T2"██"t2"╔══╝" RC"██"rc"╔══"RC"██"rc"╗" I"██"i"║" S"██╔════╝" POST
PRE T " ██║ " E "█████"e "╗ " " "T2"██"t2"║ " RC"██████"rc"╔╝" I"██"i"║" S"███████╗" POST
PRE T " ██║ " E "██"e "╔══╝ " " "T2"██"t2"║ " RC"██"rc"╔══"RC"██"rc"╗" I"██"i"║" S"╚════██║" POST
PRE T " ██║ " E "███████"e "╗" " "T2"██"t2"║ " RC"██"rc"║ "RC"██"rc"║" I"██"i"║" S"███████║" POST
PRE T " ╚═╝ " e "╚══════" "╝" " "t2"╚═" "╝ " rc"╚═╝ ╚═╝" i"╚═" "╝" S"╚══════╝" POST
"\n"
PRE " PRESS ANY KEY TO BEGIN\n"
"\n"
;
// welcometext.h
extern const char welcomestring[];
// main.c
#include <stdio.h>
int main() {
printf("%s\n", welcomestring);
}
Note that L"string", u"string" and U"string" have meaning in C, and R"string" is interpreted by GCC as a raw string literal, so I renamed it RC. It would be good to pick unique names for defines, to name these single letters something more unique, or add more spaces.
I have a set of #defines like these:
#define MODULE1_PINMASK 0x1
#define MODULE2_PINMASK 0x2
#define MODULE3_PINMASK 0x3
where the value of the pinmask depends on the second argument of:
#define MODULE1_PORT_PIN A,1
#define MODULE2_PORT_PIN A,2
#define MODULE3_PORT_PIN A,3
If at any point in future, I make a change, e.g:
#define MODULE1_PORT_PIN A,1 /* changes to #define MODULE1_PORT_PIN A,4 */
I need to also change the pinmask:
#define MODULE1_PINMASK 0x1 /* then becomes #define MODULE1_PINMASK 0x4 */
I'm trying to automate the process by not having to manually change the pinmask. So far I've got these macros to extract the second argument of MODULEX_PORT_PIN (I don't care about the first argument in this case):
#define GET_SECOND(X, Y) Y
#define GET_PIN(PORT_PIN) GET_SECOND(PORT_PIN)
If i use them in functions, I get the correct result, for instance:
uint8_t pinmask=0x0;
switch (GET_PIN(MODULE2_PORT_PIN))
{
case 1:
pinmask = 0x1;
break;
case 2:
pinmask = 0x2;
break;
case 3:
pinmask = 0x3;
break;
default:
break;
}
printf ("%#x", pinmask); /* prints "0x2" */
but I want to keep the pinmasks as #defines. Is there a way to implement a #define GET_PINMASK macro which uses the switch case to define the pinmask? I'm aiming for something like:
#define MODULE1_PINMASK ASSIGN_PINMASK(GET_PIN(MODULE1_PORT_PIN))
which in this case would define MODULE1_PINMASK as 0x1.
EDIT: The second argument in #define MODULE1_PORT_PIN A,1 is an uint8_t and not a hex value and so I can't pass it directly.
I think you may be overthinking the problem. If the second field of each MODULEn_PORT_PIN define is always an integer constant expression, then this should work:
#define MODULE1_PORT_PIN A,1
#define MODULE2_PORT_PIN A,2
#define MODULE3_PORT_PIN A,3
#define GET_SECOND(X, Y) (Y)
#define PIN_TO_MASK(PIN) (1ul << GET_SECOND(PIN))
#define MODULE1_PINMASK PIN_TO_MASK(MODULE1_PORT_PIN)
#define MODULE2_PINMASK PIN_TO_MASK(MODULE2_PORT_PIN)
#define MODULE3_PINMASK PIN_TO_MASK(MODULE3_PORT_PIN)
It is not clear from your question whether the second field can be something other than an integer constant expression. If the second field ever involves an enum constant, then the MODULEn_PINMASK macros can still be used in any context except for #if expressions. If it ever involves a variable, then they can only be used inside the body of a function. (Since this is C and not C++, that's true even if the variable is const.)
There is no way to avoid having to write each #define individually. If that is a problem, you should be thinking about writing a program that generates the list of #defines. Generating source code from a DSL of your own invention, at build time, is an under-valued technique.
Have you considered using x-macros?
You start by creating an abstract #define for the list of entries:
#define CREATE_LIST() \
ENTRY(1, A, 0x1) \
ENTRY(2, A, 0x2) \
ENTRY(3, A, 0x3)
And then invoke the list for different definitions of ENTRY:
// Get the number of entries. Creates something like:
// const uint8_t PIN_COUNT = 0 + 1 + 1 + 1;
#define ENTRY(number, x, y) + 1
const uint8_t PIN_COUNT = \
CREATE_LIST()
;
#undef ENTRY
// Array of first parameters
#define ENTRY(number, x, y) #x ,
const char * Pin_names[PIN_COUNT] =
{
CREATE_LIST()
};
#undef ENTRY
// Array of second parameters
#define ENTRY(number, x, y) y,
const uint8_t Pin_masks[PIN_COUNT] =
{
CREATE_LIST()
};
#undef ENTRY
// Array of module names
#define ENTRY(number, x, y) STRINGIFY(MODULE ## number) ,
const char * Module_names[PIN_COUNT] =
{
CREATE_LIST()
};
#undef ENTRY
The preprocessor will expand this to something like:
const uint8_t PIN_COUNT =
+ 1 + 1 + 1
;
const char * Pin_names[PIN_COUNT] =
{
"A" , "A" , "A" ,
};
const uint8_t Pin_masks[PIN_COUNT] =
{
0x1, 0x2, 0x3,
};
const char * Module_names[PIN_COUNT] =
{
"MODULE1", "MODULE2", "MODULE3"
};
The possibilities are endless. It's less readable, but perhaps slightly more maintainable.
I'm wondering how operator passing into macros works. They are macros from glib sources (glib/testutils.h).
In code you are using assert as g_assert_cmpint(1, ==, 2);, so operator just passed as is. How it works? What does # token mean in this macro?
#define g_assert_cmpint(n1, cmp, n2) \
G_STMT_START { \
gint64 __n1 = (n1), __n2 = (n2); \
if (__n1 cmp __n2) ; \
else \
g_assertion_message_cmpnum( \
G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
#n1 " " #cmp " " #n2, __n1, #cmp, __n2, 'i'); \
} G_STMT_END
and g_assert_message_cmpnum has such interface:
void g_assertion_message_cmpnum(const char *domain, \
const char *file,\
int line,\
const char func,\
const char *expr,\
long double arg1, \
const char *cmp,\
long double arg2,\
char numtype);
does this mean that # converts cmp operator to a string?
But then how to understand this line #n1 " " #cmp " " #n2 from a macro?
Because macros are processed before compile time (preprocessor stage). So it would simply "replaced" cmp with the passed operator.
Using "macros" is unsafed because it doesn't perform type checked.
Example bellow:
#define DOUBLE(x) x << 1 /// shift 1 bit left = multiple by 2
cout << DOUBLE(5) << endl; /// result "51" instead of "10"
/// because the result of processed code is:
cout << 5 << 1 << endl;
Yes # in the preprocessor is "stringification".
Then, in a later phase after preprocessing, all adjacent string literals are joined into one big string.
I have different Address in Macro's. Which I need to pick any of the address depends on my application. Here the Details below.
#define Location1_Subset1_Sub1 0x011F
#define Location1_Subset1_Sub2 0x0150
#define Location1_Subset1_Sub3 0x0170
#define Location1_Subset2_Sub1 0x0190
#define Location1_Subset2_Sub2 0x01AF
#define Location1_Subset2_Sub3 0x01EF
#define Location2_Subset1_Sub1 0x0211
#define Location2_Subset1_Sub2 0x0230
#define Location2_Subset1_Sub3 0x0240
#define Location2_Subset2_Sub1 0x027F
#define Location2_Subset2_Sub2 0x02A0
#define Location2_Subset2_Sub3 0x02EF
The above Macros is for Address.
if(cond)
{
var1 = 1;
if(cond)
{
var2 = 2;
}
if(cond)
{
var3 = 1;
}
}
uint32 = Read_Address = fn(var1, var2, var3);
This is an example of my application. Based on the var1, var2 and var3, macro should pick the respective address. According to example above. It should pick the Address Location1_Subset2_sub1.
I need to define one macro, which will concatenate the variable. I tried with below macro, which is not right.
#define fn(var1,var2,var3) (Location##var1_Subset##var2_sub##var3)
It is concat the string "Locationvar1_Subsetvar2_subvar3". But I want which will concate the value in var's. I Would be thankful, if some one guide me.
Macros and variables live in entirely different worlds: they cannot read the value of variables. Macros are expanded during the preprocessing stage, so your program isn't even compiled yet. They can only do purely textual manipulation of your source code.
Consider storing your constants in a static array:
static const uint32 fn[2][2][3] = {
{
{0x011F, 0x0150, 0x0170},
{0x0190, 0x01AF, 0x01EF}
},
/* ... */
};
Then you can access them directly with var1 to var3 as indices:
uint32 Read_Address = fn[var1 - 1][var2 - 1][var3 - 1];
Use this source code to concat the strings.
#define fn(var1,var2,var3) (Location##var1##_Subset##var2##_sub##var3)
But in your program, you can't do through this way.Becase Macro is processed in pre-compile time,not in running time.
I've written a program to probe the limits of a system's C time.h functions and dump them out in JSON. Then other things which depend on those functions can know their limits.
# system time.h limits, as JSON
{
"gmtime": {
"max": 2147483647,
"min": -2147483648
},
"localtime": {
"max": 2147483647,
"min": -2147483648
},
"mktime": {
"max": {
"tm_sec": 7,
"tm_min": 14,
"tm_hour": 19,
"tm_mday": 18,
"tm_mon": 0,
"tm_year": 138,
"tm_wday": 1,
"tm_yday": 17,
"tm_isdst": 0
},
"min": {
"tm_sec": 52,
"tm_min": 45,
"tm_hour": 12,
"tm_mday": 13,
"tm_mon": 11,
"tm_year": 1,
"tm_wday": 5,
"tm_yday": 346,
"tm_isdst": 0
}
}
}
gmtime() and localtime() are simple enough, they just take numbers, but mktime() takes a tm struct. I wrote a custom function to turn a tm struct into a JSON hash.
/* Dump a tm struct as a json fragment */
char * tm_as_json(const struct tm* date) {
char *date_json = malloc(sizeof(char) * 512);
#ifdef HAS_TM_TM_ZONE
char zone_json[32];
#endif
#ifdef HAS_TM_TM_GMTOFF
char gmtoff_json[32];
#endif
sprintf(date_json,
"\"tm_sec\": %d, \"tm_min\": %d, \"tm_hour\": %d, \"tm_mday\": %d, \"tm_mon\": %d, \"tm_year\": %d, \"tm_wday\": %d, \"tm_yday\": %d, \"tm_isdst\": %d",
date->tm_sec, date->tm_min, date->tm_hour, date->tm_mday,
date->tm_mon, date->tm_year, date->tm_wday, date->tm_yday, date->tm_isdst
);
#ifdef HAS_TM_TM_ZONE
sprintf(&zone_json, ", \"tm_zone\": %s", date->tm_zone);
strcat(date_json, zone_json);
#endif
#ifdef HAS_TM_TM_GMTOFF
sprintf(&gmtoff_json", \"tm_gmtoff\": %ld", date->tm_gmtoff);
strcat(date_json, gmtoff_json);
#endif
return date_json;
}
Is there a way to do this generically, for any given struct?
Note: C, not C++.
Not in C—at least in general. But if the C module is compiled with debug symbols, and the object module is available, you could parse that and discover everything about the structure. I bet there's a library for your system to assist with that.
Having come across the same issue, i wrote one myself. https://github.com/jamie-pate/jstruct . It's written to allow annotating existing c structures, then generate meta-data information based on the annotations. The library reads the metadata to import/export c structures to json strings and back. A python script takes care of parsing the annotated header and generating new headers and metadata initializers. The jstruct library uses https://github.com/json-c/json-c internally.
I have also noticed https://github.com/marel-keytech... but that was after writing the entire thing. (and info on that project's page is sparse)
There's no support yet for annotating a single struct from an existing system lib but you could wrap the tm struct in an annotated custom struct. (I think?)
Feel free to add features requests or even pull requests if you have ideas that would make the library more useful to you. One idea I had would be to add a way to embed that kind of read only struct inside an annotated wrapper with a #inline annotation: eg (currently unsupported but could be added)
//#json
struct my_time {
//#inline
struct tm tm;
}
Code:
struct my_time t;
mktime(&t.tm);
struct json_object *result = jstruct_export(t, my_time);
In the mean time you could do the same thing without #inline (since it hasn't been written yet) and just extract the tm property by hand with json_object_to_json_string(json_object_object_get(result, "tm"))
This won't quite give you what you're asking for, but it might help a little:
#define NAME_AND_INT(buf, obj, param) \
sprintf((buf), "\"%s\": %d, ", #param, (obj)->(param))
You could then iterate, e.g. something like (note: not tested; consider this pseudo-code):
char * tm_as_json(const struct tm* date) {
/* ... */
char buf[BUFSIZ]; /* or, use your date_json */
pos = buf; /* I note you use the equivalent of &buf -- that works too */
/* (not sure which is "better", but I've always left the & off
* things like that -- buf is essentially a pointer, it's just
* been allocated in a different way. At least that's how I
* think of it. */
pos += NAME_AND_INT(pos, date, tm_sec);
pos += NAME_AND_INT(pos, date, tm_min);
/* ... more like this ... */
/* strip trailing ", " (comma-space): */
pos-=2;
*pos = '\0';
/* ... */
}
You could similarly define NAME_AND_STRING, NAME_AND_LONG, etc. (for tm_zone and tm_gmtoff) as needed.
Again, it's not a generic solution, but it at least gets you a little closer, maybe.
Disclaimer: I'm the owner of the project https://github.com/tamask1s/zax-parser
With the help of the library, you can convert a C struct to JSON if you provide some information on your struct members which needs to be converted. There will be no need to include generated code in your project, but you will need a c++11 compiler in order to use it.
The lib is quite immature because I have implemented only the features I needed, but you may extend it, or you may use it as inspiration.
Example:
#define some_json_properties JSON_PROPERTY(x), JSON_PROPERTY(s)
struct some_class
{
int x = 9;
std::string s = "something";
ZAX_JSON_SERIALIZABLE(some_class, some_json_properties)
};
std::string some_json = some_obj;
---some_json's value:---
{"x":9, "s":"something"}
Nesting of objects is also possible, please check this example: https://tamask1s.github.io/zax-parser/index.html#Parsing_of_structures_with_fields_of_serializable_structures
Tom Christiansen once wrote pstruct/h2ph which is in perl CORE to parse .stabs info from the used compiler, and create readable info for all data structures.
C structs into JSON is trivial based on h2ph.
http://perl5.git.perl.org/perl.git/blob/HEAD:/utils/h2ph.PL
This macro does not do exactly what you want (generate JSON dump of C data), but I think it shows some possibility. You can dump content of any C data with a "p(...);" call.
I used gdb as external helper to make this work, but it is possible to implement one with libbfd. In that case, you can fully control your output - like generating JSON compatible output.
#ifndef PP_H
#define PP_H
/*
* Helper function (macro) for people who loves printf-debugging.
* This dumps content of any C data/structure/expression without prior
* knowledge of actual format. Works just like "p" or "pp" in Ruby.
*
* Usage:
* p(anyexpr);
*
* NOTE:
* - Program should be compiled with "-g" and preferrably, with "-O0".
*
* FIXME:
* - Would be better if this doesn't depend on external debugger to run.
* - Needs improvement on a way prevent variable from being optimized away.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
// Counts number of actual arguments.
#define COUNT_(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define COUNT(...) COUNT_(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)
// Dispatches macro call by number of actual arguments.
// Following is an example of actual macro expansion performed in case
// of 3 arguments:
//
// p(a, b, c)
// -> FUNC_N(p, COUNT(a, b, c), a, b, c)
// -> FUNC_N(p, 3, a, b, c)
// -> p_3(a, b, c)
//
// This means calling with simple "p(...)" is fine for any number of
// arguments, simulating "true" variadic macro.
#define CONCAT(name, count) name##count
#define FUNC_N(name, count, ...) CONCAT(name, count)(__VA_ARGS__)
// Forbids variable from being optimized out, so debugger can access it.
//
// FIXME:
// - Current implementation does not work with certain type of symbols
#define ENSURE(...) FUNC_N(ENSURE_, COUNT(__VA_ARGS__), __VA_ARGS__)
#define ENSURE_1(a) asm(""::"m"(a))
#define ENSURE_2(a, ...) do { ENSURE_1(a); ENSURE_1(__VA_ARGS__); } while (0)
#define ENSURE_3(a, ...) do { ENSURE_1(a); ENSURE_2(__VA_ARGS__); } while (0)
#define ENSURE_4(a, ...) do { ENSURE_1(a); ENSURE_3(__VA_ARGS__); } while (0)
#define ENSURE_5(a, ...) do { ENSURE_1(a); ENSURE_4(__VA_ARGS__); } while (0)
#define ENSURE_6(a, ...) do { ENSURE_1(a); ENSURE_5(__VA_ARGS__); } while (0)
#define ENSURE_7(a, ...) do { ENSURE_1(a); ENSURE_6(__VA_ARGS__); } while (0)
#define ENSURE_8(a, ...) do { ENSURE_1(a); ENSURE_7(__VA_ARGS__); } while (0)
// Dumps content of given symbol (uses external GDB for now)
//
// NOTE:
// - Should use libbfd instead of gdb? (but this adds complexity...)
#define PP_D(...) do { \
char *arg[] = { __VA_ARGS__, NULL }; \
char **argp = arg; \
char cmd[1024]; \
FILE *tmp = tmpfile(); \
fprintf(tmp, "attach %d\n", getpid()); \
fprintf(tmp, "frame 2\n"); \
while (*argp) \
fprintf(tmp, "p %s\n", *argp++); \
fprintf(tmp, "detach\n"); \
fflush(tmp); \
sprintf(cmd, "gdb -batch -x /proc/%d/fd/%d", \
getpid(), fileno(tmp)); \
system(cmd); \
fclose(tmp); \
} while (0)
#define PP(...) do { \
FUNC_N(PP_, COUNT(__VA_ARGS__), __VA_ARGS__); \
ENSURE(__VA_ARGS__); \
} while (0)
#define PP_1(a) do { PP_D(#a); } while (0)
#define PP_2(a,b) do { PP_D(#a,#b); } while (0)
#define PP_3(a,b,c) do { PP_D(#a,#b,#c); } while (0)
#define PP_4(a,b,c,d) do { PP_D(#a,#b,#c,#d); } while (0)
#define PP_5(a,b,c,d,e) do { PP_D(#a,#b,#c,#d,#e); } while (0)
#define PP_6(a,b,c,d,e,f) do { PP_D(#a,#b,#c,#d,#e,#f); } while (0)
#define PP_7(a,b,c,d,e,f,g) do { PP_D(#a,#b,#c,#d,#e,#f,#g); } while (0)
#define PP_8(a,b,c,d,e,f,g,h) do { PP_D(#a,#b,#c,#d,#e,#f,#g,#h); } while (0)
// Comment this out if you think this is too aggressive.
#define p PP
#endif
Indentation is lost in above paste, but you can grab the source from: https://github.com/tai/ruby-p-for-c