I’m trying to fix some proprietary compiler directives by using a filter. I discovered that if I pass a file through a filter, the file documentation is generated correctly, but auto-linking seems broken!
I came up with a very simple example. My filter is implemented in Python and it just opens the file and prints every single line, it doesn’t change anything. I confirmed this by comparing the filter’s input and output (by redirection) with a diff and there are no differences at all.
I redirected the filter’s output to a file called filtered_c.h. If I include this file as a source file, I get auto-link to work. If I pass the original header file through the filter, auto-linking is broken and I get a warning
latex_double.md:7: warning: unable to resolve reference to `some_c_function' for \ref command
If I comment out the FILTER_PATTERNS line in the Doxyfile, auto-linking works.
This is really an issue for me, as I’m also supporting parsing assembly files through a filter script and I see the same behavior there: the file documentation is perfect, but no auto-linking.
I'm using Doxygen 1.8.15 in windows 10.
Any thoughts are very welcome! Thanks!
Doxyfile:
# General options
PROJECT_NAME = "Double table headers in LaTeX"
INPUT = doc/latex_double.md
INPUT += include/regular_c.h
#INPUT += filtered_c.h
#You should not need to change the rest of the file
OUTPUT_DIRECTORY = output_doc
GENERATE_HTML = YES
GENERATE_TREEVIEW = YES
GENERATE_LATEX = YES
OPTIMIZE_OUTPUT_FOR_C = YES
FILTER_PATTERNS += *.h="c:\Python27\python filter\dummy_filter.py"
Filter Script:
from __future__ import print_function
import sys
if len(sys.argv) != 2:
sys.exit(1)
def extract_globals(filename):
with open(filename, 'r') as infile:
for line in infile:
print(line, end='')
extract_globals(sys.argv[1])
Markdown source:
# Chapter Title {#chapter_title}
Some text here
# Introduction {#chapter_intro}
Loren Ipsum #ref some_c_function
regular_c.h
/**
* #file regular_c.h
* #brief This is a simple C header file with some examples.
*/
#ifndef REGULAR_C_H_
#define REGULAR_C_H_
/* ----------------------------------------------------------------------------
* If building with a C++ compiler, make all of the definitions in this header
* have a C binding.
* ------------------------------------------------------------------------- */
#ifdef __cplusplus
extern "C"
{
#endif /* ifdef __cplusplus */
/* ----------------------------------------------------------------------------
* Include files
* --------------------------------------------------------------------------*/
/* ----------------------------------------------------------------------------
* Defines
* --------------------------------------------------------------------------*/
/* ----------------------------------------------------------------------------
* Global variables and types
* --------------------------------------------------------------------------*/
/**
* This enumeration has the return codes for the #ref some_c_function function.
*/
typedef enum __SOME_C_FUNCTION_RETURN {
SOME_C_FUNCTION_RETURN_everthing_ok = 0, ///< Nothing to see here
SOME_C_FUNCTION_RETURN_some_weird_error, ///< This is not the error you're looking for
SOME_C_FUNCTION_RETURN_some_very_bad_error, ///< This is not the error we are looking for
SOME_C_FUNCTION_RETURN_life_is_sad_just_cope ///< Move along
} SOME_C_FUNCTION_RETURN;
/* ----------------------------------------------------------------------------
* Function prototype definitions
* --------------------------------------------------------------------------*/
/**
* #brief Some C function that is here just as an example.
*
* This function doesn't actually do anything useful.
*
* #param[in] v We do something with this value.
* #param[out] ptr Some value will be written here.
*
* #return Returns a value that depends on what happened:
* - #ref SOME_C_FUNCTION_RETURN_everthing_ok
* Uh, everything is under control. Situation normal.
* - #ref SOME_C_FUNCTION_RETURN_some_weird_error
* Uh, had a slight weapons malfunction.
* - #ref SOME_C_FUNCTION_RETURN_some_very_bad_error
* We had a reactor leak here now. Give us a few minutes to lock it down.
* Large leak... very dangerous.
* - #ref SOME_C_FUNCTION_RETURN_life_is_sad_just_cope
* Who is this?? What's your operating number?
*
*/
int some_c_function(uint32_t v, void* ptr);
/* ----------------------------------------------------------------------------
* Close the 'extern "C"' block
* ------------------------------------------------------------------------- */
#ifdef __cplusplus
}
#endif /* ifdef __cplusplus */
#endif /* REGULAR_C_H_ */
filtered_c.h
/**
* #file filtered_c.h
* #brief This is a simple C header file with some examples.
*/
#ifndef REGULAR_C_H_
#define REGULAR_C_H_
/* ----------------------------------------------------------------------------
* If building with a C++ compiler, make all of the definitions in this header
* have a C binding.
* ------------------------------------------------------------------------- */
#ifdef __cplusplus
extern "C"
{
#endif /* ifdef __cplusplus */
/* ----------------------------------------------------------------------------
* Include files
* --------------------------------------------------------------------------*/
/* ----------------------------------------------------------------------------
* Defines
* --------------------------------------------------------------------------*/
/* ----------------------------------------------------------------------------
* Global variables and types
* --------------------------------------------------------------------------*/
/**
* This enumeration has the return codes for the #ref some_c_function function.
*/
typedef enum __SOME_C_FUNCTION_RETURN {
SOME_C_FUNCTION_RETURN_everthing_ok = 0, ///< Nothing to see here
SOME_C_FUNCTION_RETURN_some_weird_error, ///< This is not the error you're looking for
SOME_C_FUNCTION_RETURN_some_very_bad_error, ///< This is not the error we are looking for
SOME_C_FUNCTION_RETURN_life_is_sad_just_cope ///< Move along
} SOME_C_FUNCTION_RETURN;
/* ----------------------------------------------------------------------------
* Function prototype definitions
* --------------------------------------------------------------------------*/
/**
* #brief Some C function that is here just as an example.
*
* This function doesn't actually do anything useful.
*
* #param[in] v We do something with this value.
* #param[out] ptr Some value will be written here.
*
* #return Returns a value that depends on what happened:
* - #ref SOME_C_FUNCTION_RETURN_everthing_ok
* Uh, everything is under control. Situation normal.
* - #ref SOME_C_FUNCTION_RETURN_some_weird_error
* Uh, had a slight weapons malfunction.
* - #ref SOME_C_FUNCTION_RETURN_some_very_bad_error
* We had a reactor leak here now. Give us a few minutes to lock it down.
* Large leak... very dangerous.
* - #ref SOME_C_FUNCTION_RETURN_life_is_sad_just_cope
* Who is this?? What's your operating number?
*
*/
int some_c_function(uint32_t v, void* ptr);
/* ----------------------------------------------------------------------------
* Close the 'extern "C"' block
* ------------------------------------------------------------------------- */
#ifdef __cplusplus
}
#endif /* ifdef __cplusplus */
#endif /* REGULAR_C_H_ */
I have got some problems trying to programm the HMAC_MD5 code.
I am working in C on a STM32F4 microprocessor.
Here is my (updated) code:
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_HASH, ENABLE); static uint8_t
static Challenge[16] = "ldopwhjtsnkiaq8f";
static uint8_t Key[16] = "abcdefghijklmnop";
static uint8_t* HMAC_Key;
static uint8_t* HMAC_Input;
static uint8_t HMAC_Response1[16];
static uint8_t HMAC_Response2[16];
int m = 0;
HMAC_Input = &Challenge[0];
HMAC_Key = &Key[0];
ErrorStatus Result = ERROR;
for(m=0;m<16;m++){
HMAC_Response1[m]=1;
HMAC_Response2[m]=2;
}
Result = HASH_MD5(HMAC_Input, 16, HMAC_Response1);
Result = HMAC_MD5(HMAC_Key, 16, HMAC_Input, 16, HMAC_Response2);
That is the official description of the HMAC_MD5 function (https://github.com/espruino/Espruino/blob/master/targetlibs/stm32f4/lib/stm32f4xx_hash_md5.c):
/**
* #brief Compute the HMAC MD5 digest.
* #param Key: pointer to the Key used for HMAC.
* #param Keylen: length of the Key used for HMAC.
* #param Input: pointer to the Input buffer to be treated.
* #param Ilen: length of the Input buffer
* #param Output: the returned digest
* #retval An ErrorStatus enumeration value:
* - SUCCESS: digest computation done
* - ERROR: digest computation failed
*/
ErrorStatus HMAC_MD5(uint8_t *Key, uint32_t Keylen, uint8_t *Input,
uint32_t Ilen, uint8_t Output[16])
The function returns the value "SUCCESS" but the digest "Output" is still empty (full of '\0').
I don't get any warning from the compiler (Attolic TrueStudio) and I cannot change the value of the Key or of the Challenge (Concatenation), because the server is already running with older systems.
Let me guess. You're using STM32F405 or STM32F407, right? These parts lack the hash processor, and thus always return zeros for the digest. ST kind of documented it by opening the section on hash processor in the manual with "This section applies to STM32F415/417xx and STM32F43xxx devices.", but a quick Google search demonstrates that you are not the first person that expected this sort of information to be present in a more prominent place (datasheet, family comparison doc, product selector, etc).
And yes, I fully agree that an MCU with a missing hash processor should not report success on operations that use said hash processor, but that's apparently not how folks at ST roll.
Anyway. You will need to upgrade to SMT32F415 (or STM32F417) to get hardware accelerated hashing. If that's not an option, well, there is always a software implementation.
I had the same problem that you had using STM32 Hashing hardware. After a few tries I decided to use a md5 library
Since I'm using lwip in my project I noticed that LWIP has a md5 module inside ppp.
Just get the files needed (md5.c, md5.h) from lwip (inside lwip /src/netif/ppp/md5.c), and copy it to your proyect.
Change the non-working line
uint32_t dev=HASH_MD5((uint8_t *) input, strlen((char *) input), Md5);
for
MD5_CTX mdContext;
MD5Init(&mdContext);
MD5Update(&mdContext, input, strlen((char *) input));
MD5Final(Md5,&mdContext);
Edited: Since i dont use ppp in the project, i have copied the md5 file from ppp to the project and I edited it a bit, removing all include references (except md5.h and string.h) and removing conditional compilation:
This is the stuff that I removed at the beginning
//#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
//#if CHAP_SUPPORT || MD5_SUPPORT
//#include "ppp.h"
//#include "pppdebug.h"
And this at the end:
//#endif /* CHAP_SUPPORT || MD5_SUPPORT */
//#endif /* PPP_SUPPORT */
You can download the source code for md5.c and md5.h here
https://github.com/goertzenator/lwip/blob/master/lwip-1.4.0/src/netif/ppp/md5.c
https://github.com/goertzenator/lwip/blob/master/lwip-1.4.0/src/netif/ppp/md5.h
I would like to define a macro that will help me to auto generate offsets. Something like this:
#define MEM_OFFSET(name, size) ...
MEM_OFFSET(param1, 1);
MEM_OFFSET(param2, 2);
MEM_OFFSET(param3, 4);
MEM_OFFSET(param4, 1);
should generate the following code:
const int param1_offset = 0;
const int param2_offset = 1;
const int param3_offset = 3;
const int param4_offset = 7;
or
enum {
param1_offset = 0,
param2_offset = 1,
param3_offset = 3,
param4_offset = 7,
}
or even (not possible using C-preprocessor only for sure, but who knows ;)
#define param1_offset 0
#define param2_offset 1
#define param3_offset 3
#define param4_offset 7
Is it possible to do without running external awk/bash/... scripts?
I'm using Keil C51
It seems I've found a solution with enum:
#define MEM_OFFSET(name, size) \
name ## _offset, \
___tmp__ ## name = name ## _offset + size - 1, // allocate right bound offset and introduce a gap to force compiler to use next available offset
enum {
MEM_OFFSET(param1, 1)
MEM_OFFSET(param2, 2)
MEM_OFFSET(param3, 4)
MEM_OFFSET(param4, 1)
};
In the comments to your post you mention that you're managing an EEPROM memory map, so this answer relates to managing memory offsets rather than answering your specific question.
One way to manage EEPROM memory is with the use of a packed struct. ie, one where there is no space between each of the elements. The struct is never instantiated, it is only used for offset calculations.
typedef struct {
uint8_t param1;
#ifdef FEATURE_ENABLED
uint16_t param2;
#endif
uint8_t param3;
} __packed eeprom_memory_layout_t;
You could then use code like the following to determine the offset of each element as needed(untested). This uses the offsetof stddef macro.
uint16_t read_param3(void) {
uint8_t buf;
eeprom_memory_layout_t * ee;
/* eeprom_read(offset, size, buf) */
eeprom_read(offsetof(eeprom_memory_layout_t, param3), sizeof(ee->param3), &buf);
return buf;
}
Note that the struct is never instantiated. Using a struct like this makes it easy to see your memory map at a glance, and macros can easily be used to abstract away the calls to offsetof and sizeof during access.
If you want to create several structures based on some preprocessor declarations, you could do something like:
#define OFFSET_FOREACH(MODIFIER) \
MODIFIER(1) \
MODIFIER(2) \
MODIFIER(3) \
MODIFIER(4)
#define OFFSET_MODIFIER_ENUM(NUM) param##NUM##_offset,
enum
{
OFFSET_FOREACH(OFFSET_MODIFIER_ENUM)
};
The preprocessor would then produce the following code:
enum
{
param1_offset,
param2_offset,
param3_offset,
param4_offset,
}
I'm sure somebody will figure a nice preprocessor trick to compute the offset values with the sum of its predecessors :)
If you are doing this in C code, you have to keep in mind that const int declarations do not declare constants in C. To declare a named constant you have to use either enum or #define.
If you need int constants specifically, then enum will work well, although I the auto-generation part might be tricky in any case. Off the top of my head I can only come up with something as ugly as
#define MEM_OFFSET_BEGIN(name, size)\
enum {\
name##_OFFSET = 0,\
name##_SIZE__ = size,
#define MEM_OFFSET(name, size, prev_name)\
name##_OFFSET = prev_name##_OFFSET + prev_name##_SIZE__,\
name##_SIZE__ = size,
#define MEM_OFFSET_END()\
};
and then
MEM_OFFSET_BEGIN(param1, 1)
MEM_OFFSET(param2, 2, param1)
MEM_OFFSET(param3, 4, param2)
MEM_OFFSET(param4, 1, param3)
MEM_OFFSET_END()
Needless to say, the fact that it requires the next offset declaration to refer to the previous offset declaration by name defeats most of the purpose of this construct.
Try something like:
#define OFFSET(x) offsetof(struct {\
char param1[1], param2[2], param3[4], param4[1];\
},x)
Then you can use OFFSET(param1), etc. and it's even an integer constant expression.
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