Understanding a function definition in a header file - c

I am porting a software written to a specific microcontroller to another microcontroller but I have a problem in C language.
I would like to draw attention to the below mentioned functions defined inside hal_spi_rf_trxeb.c file. Although I searched within the file I was not able to find the full function description for the following functions.
TRXEM_SPI_WAIT_DONE()
TRXEM_SPI_RX()
TRXEM_SPI_WAIT_DONE()
TRXEM_SPI_WAIT_TX_DONE()
TRXEM_SPI_RX()
TRXEM_SPI_WAIT_MISO_LOW(x)
After a bit more searching I figured that these functions exist in the header file itself. More specifically inhal_spi_rf_trxeb.h file.
/******************************************************************************
* #fn trx16BitRegAccess
*
* #brief This function performs a read or write in the extended adress
* space of CC112X.
*
* input parameters
*
* #param accessType - Specifies if this is a read or write and if it's
* a single or burst access. Bitmask made up of
* RADIO_BURST_ACCESS/RADIO_SINGLE_ACCESS/
* RADIO_WRITE_ACCESS/RADIO_READ_ACCESS.
* #param extAddr - Extended register space address = 0x2F.
* #param regAddr - Register address in the extended address space.
* #param *pData - Pointer to data array for communication
* #param len - Length of bytes to be read/written from/to radio
*
* output parameters
*
* #return rfStatus_t
*/
rfStatus_t trx16BitRegAccess(uint8 accessType, uint8 extAddr, uint8 regAddr, uint8 *pData, uint8 len)
{
uint8 readValue;
<span style="background-color:#ff0000;">TRXEM_SPI_BEGIN();</span>
while(TRXEM_PORT_IN & TRXEM_SPI_MISO_PIN);
/* send extended address byte with access type bits set */
<span style="background-color:#ff0000;"> TRXEM_SPI_TX</span>(accessType|extAddr);
TRXEM_SPI_WAIT_DONE();
/* Storing chip status */
readValue = TRXEM_SPI_RX();
TRXEM_SPI_TX(regAddr);
TRXEM_SPI_WAIT_DONE();
/* Communicate len number of bytes */
trxReadWriteBurstSingle(accessType|extAddr,pData,len);
hal_spi_rf_trxeb.h claims to have the function I was searching in the form of a macro. Those macros are shown below.
/******************************************************************************
* MACROS
*/
/* Macros for Tranceivers(TRX) */
#define TRXEM_SPI_BEGIN() st( TRXEM_PORT_OUT &= ~TRXEM_SPI_SC_N_PIN; NOP(); )
#define TRXEM_SPI_TX(x) st( UCB0IFG &= ~UCRXIFG; UCB0TXBUF= (x); )
#define TRXEM_SPI_WAIT_DONE() st( while(!(UCB0IFG & UCRXIFG)); )
#define TRXEM_SPI_WAIT_TX_DONE() st( while(!(UCB0IFG & UCTXIFG)); )
#define TRXEM_SPI_RX() UCB0RXBUF
#define TRXEM_SPI_WAIT_MISO_LOW(x) st( uint8 count = 200; \
while(TRXEM_PORT_IN & TRXEM_SPI_MISO_PIN) \
{ \
__delay_cycles(5000); \
count--; \
if (count == 0) break; \
} \
if(count>0) (x) = 1; \
else (x) = 0; )
Questions
I do not understand how these macros work.
Could someone please tell me how a function has been defined in the header file?
How can a function be defined in the header file itself?
What does the function st() do?
Should you require, the whole project can be downloaded by this link.
For more information you may view the linked header and source (.h and .c) files.

As you have already said, those are not functions but rather macros. You can read about macros in the gcc manual.
A macro is a fragment of code which has been given a name. Whenever
the name is used, it is replaced by the contents of the macro.
That's the summary. There's a lot more detail to it than that. But for starters you can think of macros as code replacement rules. Macros are expanded by the preprocessor before the compiler does it's job.
A simple example:
#define MY_PRINT_MACRO(string1, string2) printf("%s %s\n", string1, string2)
MY_PRINT_MACRO("hello", "world");
The preprocessor will change the second line to be:
printf("%s %s\n", "hello", "world");
And that is exactly what the compiler will see (it does not see MY_PRINT_MACRO at all).

These are not functions but macros. Macros are basically being replaced by the code they represent before compilation (preprocessing).
Macros can be defined and redefined over header files.
st() function is not written in the context so not sure what it does.

Related

Doxygen filter script breaks auto-link generation

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_ */

DT_USED entry in .dynamic section of ELF file

I am curious about DT_USED entry in .dynamic section. However, I could only find two code examples that describe this entry.
1.
#define DT_USED 0x7ffffffe /* ignored - same as needed */
in https://github.com/switchbrew/switch-tools/blob/master/src/elf_common.h
2.
case DT_USED:
case DT_INIT_ARRAY:
case DT_FINI_ARRAY:
if (do_dynamic)
{
if (entry->d_tag == DT_USED
&& VALID_DYNAMIC_NAME (entry->d_un.d_val))
{
char *name = GET_DYNAMIC_NAME (entry->d_un.d_val);
if (*name)
{
printf (_("Not needed object: [%s]\n"), name);
break;
}
}
print_vma (entry->d_un.d_val, PREFIX_HEX);
putchar ('\n');
}
break;
in http://web.mit.edu/freebsd/head/contrib/binutils/binutils/readelf.c
I want to know, what's the meaning of "Not needed object"? Does it mean that file names listed here are not needed?
In general, when looking at Solaris dynamic linker features, it is possible to find more information in the public Illumos sources (which were once derived from OpenSolaris). In this case, it seems that DT_USED is always treated like DT_NEEDED, so they are the essentially same thing. One of the header files, usr/src/uts/common/sys/link.h also contains this:
/*
* DT_* entries between DT_HIPROC and DT_LOPROC are reserved for processor
* specific semantics.
*
* DT_* encoding rules apply to all tag values larger than DT_LOPROC.
*/
#define DT_LOPROC 0x70000000 /* processor specific range */
#define DT_AUXILIARY 0x7ffffffd /* shared library auxiliary name */
#define DT_USED 0x7ffffffe /* ignored - same as needed */
#define DT_FILTER 0x7fffffff /* shared library filter name */
#define DT_HIPROC 0x7fffffff
There may have been planned something here, but it doesn't seem to be implemented (or it used to be and no longer is).

Function HMAC_MD5 : Return succes but no value

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

Best way to define offsets via C preprocessor

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.

Is there a way to dump a C struct?

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

Resources