How is the audio played in this code (C/SDL) - c

I think this is meant to be a 4k style of visualization program but written in C.
So it draws some spheres and plays some music, I am not trying to understand the graphics, that is generally maths. It also uses SDL. I am trying to understand where the music is coming from, there isn't any apparent function for the audio.
The music is initialized SDL_AudioSpec as, oa; and some parameters are set but where does the tune actually come from, is this a build in sample from SDL.
/* "Shadow Sun" */
/* tonic - http://jet.ro */
#include "SDL.h"
#include "SDL_opengl.h"
#include <math.h>
#include <stdlib.h>
#define S1(a,b,c) #a".."#b".."#c"."
#define X2(a) a a
#define X4(a) a a a a
#define S14(a,b,c) X4(S1(a,b,c))
#define S2(a) #a"."#a"....."
#define S24(a) X4 ( S2 ( a ) )
#define S4(a) #a #a #a #a
#define S3(a,b, c) S1(a,b,c)S4(..)
#define SE X4( X4( S4 (..)))
#define P(x) glDisable(GL_##x);
#define EF_(x) F[C][1638##x+M
#define B float
char l[]=SE SE X2(S3(J,H,M)S3(J,H,E)S3(F,E,A)S3(F,E,J)S3(H,E
,O)S3( H,E,C)S3 (H,F,x) S3(H,F,L)) ;
char b[] = SE X2(S4(j.jv.j.v)S4
(f.fr.f.r)S4(e.eq.e.q)S4(h.ht.h.t))SE;
char*r [3] ={S14(v,v,q)S14(r,r,m)
S14(q,q,l)S14(t,t,o),S24(J)S24(F)S24(E)S24(H),S24(M)S24(J)S24(H)S24(L) };
Uint32 S;GLUquadric*Q;
int D,R,U,L,p=0;B T,F[5][0100000]={{0},{0}};void G(
void*u,Uint8*_,int L){while(L>0){int M=p&037777,s=p>>12,H=(s%R)[l],J=(s
#define EF(C,l,s,ms) ((s*ms+EF_(4)-l]*(1-ms))*0.5f+EF_(3)]*0.5f)
%U)[b],C=0,a=0,n;
B v,q,m,E=(0x2000-(p&017777))/8192e0f;
#define FQ(s) (B)sin((0.12*pow(2,n/12.0))*p*s)
#define IZ(v,c) if(v c)v*=v;else v=0;
#define BF F[C][M]=F[C][M+16384]=q;v+=q;++C
#define IP(n,x,y,z,c) if(n!='.'){x}q=EF(C,y,q,z);c;BF;
#define IS(s,c) if(q c s 0.75f)q=s 0.75f;
#define NQ(N,a,m) n=N-(N>='a'?'a'+24:'A')+a;q=FQ(m);
v=q=0;IP(H,NQ(H,12,1)m=FQ(1.01f);IZ(q,>
0.6)IZ(m,>0.6)q+=m;q*=E*0.5f+0.5f;,
12288,0.2f,;)for(;a<3;++a){char
sn=(s%D)[a[r]];q=0;IP (sn,NQ(
sn,12,0.25f)IZ(q,<0.3),12288,
0.2f,;)}q=0;IP(J,NQ(J,0,0.125f)
X4(q*=q;)q=(B)fmod(q*20,4)*4;
,6144,0.25f,IS(-,<)IS(+,>)q*=
E)v*=0.6f;*(Sint16*)_=(Sint16
)(v*32767);_+=2;++p;L-=2;}}
#define N(x) glEnable(GL_##x);
void I(int a){B b[]={a?0.f:1,a?0:4.f,-2.f,0},c[]={0,
-2,0,0},d[]={.2f,0.f,0.f ,1},e[]={a?.5f:.8f,a?.75f
:.3f,a?1:.2f,1},f[]={0,.2f,.3f,1},g[]={0,0,0,1,1,1,1},*h=&g[3];
#define GF(l,x,v) glLightfv(GL_LIGHT##l,GL_##x,v);
GF(0,POSITION,b)GF(0,AMBIENT,d)GF(0,DIFFUSE,e)GF(0,SPECULAR,h)if(a)P(LIGHT1
)else N(LIGHT1)GF(1,POSITION,c)GF(1,AMBIENT,f)GF(1,DIFFUSE,f)GF(1,SPECULAR,g)
#define GM(t,x,v) glMaterial##t(GL_FRONT,GL_##x,v);
GM(fv,AMBIENT,g)GM(fv,DIFFUSE,h)GM(fv,SPECULAR,h)GM(
fv,EMISSION,g) GM (f,SHININESS,60) glColorMaterial (
GL_FRONT,GL_DIFFUSE);N(COLOR_MATERIAL)}
void K(int b,
B c,int d){ int a; B e=c*c,g=1e0f/e;glColor3f(1,1,1);
#define BC(v,f,p,q) B v=(B)f(ta*(sin(T*p*g)*5e-2+q*c)*g)*c
for(a=0;a<b;++a){B ta=T*1e-4f+a*c;BC(x,cos,2.3e-4,0.0867);
BC(y,sin,2e-4,0.2735);
BC(z,cos,1.5e-4,0.1243);
glPushMatrix();
glTranslatef(x,y,z);
glRotatef(50,1,0,0);
glRotatef (T*5e-2f+a*20,0,1,0);
glCallList(d);
glPopMatrix();}}
int _tmain(int c,char **a)
{ SDL_Event e;
SDL_AudioSpec as, oa;if (
SDL_Init(48)<0)
exit(1);
atexit(SDL_Quit);
SDL_SetVideoMode(640,480,32,2);
as.freq=053042;
as.format=AUDIO_S16;
as.channels=1;
as.samples=4096;
as.callback=G;
D=strlen(r[0]);
R=strlen(l);
U=strlen(b);
if(
SDL_OpenAudio(&as,&oa) <0)
exit(2);
SDL_PauseAudio(0);
Q=gluNewQuadric();
L=glGenLists(2);
#define NL_ GL_COMPILE);gluSphere(Q,0.1,40
#define NL(a,d) glNewList(L+a,NL_/d,20/d);
glEndList();
NL(0,1)NL(1,5)N(LIGHTING)N(LIGHT0)N(CULL_FACE)
glShadeModel(GL_SMOOTH);
N(BLEND)glBlendFunc(
GL_ONE,GL_ONE);
S=SDL_GetTicks();
T=0;
while(!SDL_PollEvent(&e)||((e.type!=2||e.key.keysym.sym!=27)&& e.type!=12))
{
B d,f;
Uint32 g;
f=T;
g=SDL_GetTicks()-S;T=T*0.75f+g*2.5e-1f;glClearColor(0,0,0,1);
glClear(16640);
glViewport(0,0,640,480);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45,4./3,0.5,100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
d=T*1e-4f;
gluLookAt(cos(d)*3,0,sin(d)*3,0, 0, 0, 0,1, 0);
I(0);
K(0144, 1, L);
P(COLOR_MATERIAL)I(1);K(100+0144+0x64+0620+0454,15,L+1);
P(COLOR_MATERIAL)
SDL_GL_SwapBuffers();
/* $Revision: 1.1 $*/
SDL_Delay(~0&1);
}
SDL_CloseAudio();
return 0;
}

This code is not meant to be instructive! :)
Look at the macros and you see that there are several tokenizing macros. These macros combine tokens into new tokens before the compiler sees them and are a simple and effective method for code obfuscation in C and can make the source smaller too. You will have to sift deeply through those macro expansions until you find what is happening.
The code will either call SDL_MixAudio() or directly modify the audio stream buffer in a callback. The callback pointer is stored in the SDL_AudioSpec struct passed to SDL_OpenAudio() or SDL_OpenAudioDevice(). The second parameter of the callback is the writable audio stream.
Good luck!

Related

C ansi format macro global char[] overwrite

I have a coding assignment that must be done in C. I got sidetracked and decided to try to make a format macro i can use for styling text with ansi codes. My code is as follows:
<format.h>
#include <stdarg.h>
#include <string.h>
/**
* global buffer to store format arguments for prints
*/
char format_buffer[128];
/**
* Count the number of char* in variadic arguments, auxiliary for FMT macro
*/
#define FARGS(...) (sizeof((char*[]){__VA_ARGS__})/sizeof(char*))
/**
* Automatically fills in parameters buffer and argc for the format function. Buffer is a global variable, therefore it is never out of scope.
*/
#define FMT(...) format(format_buffer, FARGS(__VA_ARGS__), __VA_ARGS__)
#define A_START "\x1B["
#define A_END "m"
/**
* creates a format string based on input parameters.
* #param argc the number of ansi flags to use
* #param buffer the buffer to write the format string into. A size of 64 should be enough for most reasonable uses
* #param ... the ansi flags to use. Examples include RED, BLUE, DASH, etc
* #return the same pointer buffer, for ease of use in printf functions
*/
char* format(char* buffer, int argc, ...);
// ansi macros for formatting, all of the style ";NN"
<format.c>
#include "format.h"
char format_buffer[128] = {};
char* format(char* buffer, int argc, ...){
buffer[0] = '\0';
va_list argv;
int i;
// initialize argv to get data from the variable arguments
va_start(argv, argc);
strcat(buffer, A_START);
/* access all the arguments assigned to valist */
for (i = 0; i < argc; i++) {
strcat(buffer, va_arg(argv, char*));
}
strcat(buffer, A_END);
/* clean memory reserved for valist */
va_end(argv);
return buffer;
}
Using that, I can call the macro as follows, which is what I want:
printf("%sHello!\n", FMT(RED, BOLD)) //prints <Hello!\n> in red and bold
The problem
The problem I have is when I try to use multiple calls in the same print statement:
printf("%sHello, %sWorld!\n", FMT(RED, BOLD), FMT(YELLOW)) //prints <Hello, World!\n> in yellow
I'm positive it's not working as expected because FMT(...) always returns the same global char*, but I don't know how could i change it so that:
I can call the format macro as shown above: printf("%sHello!\n", FMT(RED, BOLD)).
I can use multiple FMT calls in the same print statement, as in printf("%sHello, %sWorld!\n", FMT(RED, BOLD), FMT(YELLOW)) should print <Hello, > in red and bold and <World!\n> in yellow.
As a final note, I'd rather code the solution instead of using a library or header that already has an implementation of this.
I first tried creating a new char[] inside the format function, but it would be stored in the stack so I assume that's a much worse outcome.
Simply change the macro to:
#define FMT(...) format((char[128]){}, FARGS(__VA_ARGS__), __VA_ARGS__)
https://godbolt.org/z/KvWWcYfPb
After reading both comments from Jonathan Leffler and the answer from 0___________ I've changed the files to this:
<format.h>
#include <stdarg.h>
#include <string.h>
/**
* Count the number of char* in variadic arguments, auxiliary for FMT macro
*/
#define FARGS(...) (sizeof((char*[]){__VA_ARGS__})/sizeof(char*))
#define FMT_BUF_SIZE 64
/**
* Automatically fills in parameters buffer and argc for the format function.
*/
#define FMT(...) format((char[FMT_BUF_SIZE]){""}, FARGS(__VA_ARGS__), __VA_ARGS__)
#define FMT_START "\x1B["
#define FMT_END "m"
// ansi flags for text styling, there's a lot of them
//black
#define BLACK ";30"
//red
#define RED ";31"
//green
#define GREEN ";32"
//yellow
#define YELLOW ";33"
//blue
#define BLUE ";34"
//magenta
#define MAGENTA ";35"
//cyan
#define CYAN ";36"
//bright gray
#define GRAY_B ";37"
//black background
#define BLACK_BG ";40"
//red background
#define RED_BG ";41"
//green background
#define GREEN_BG ";42"
//yellow background
#define YELLOW_BG ";43"
//blue background
#define BLUE_BG ";44"
//magenta background
#define MAGENTA_BG ";45"
//cyan background
#define CYAN_BG ";46"
//bright gray background
#define GRAY_BG_B ";47"
//gray
#define GRAY ";90"
//bright red
#define RED_B ";91"
//bright green
#define GREEN_B ";92"
//bright yellow
#define YELLOW_B ";93"
//bright blue
#define BLUE_B ";94"
//bright magenta
#define MAGENTA_B ";95"
//bright cyan
#define CYAN_B ";96"
//white
#define WHITE ";97"
//gray background
#define GRAY_BG ";100"
//bright red background
#define RED_BG_B ";101"
//bright green background
#define GREEN_BG_B ";102"
//bright yellow background
#define YELLOW_BG_B ";103"
//bright blue background
#define BLUE_BG_B ";104"
//bright magenta background
#define MAGENTA_BG_B ";105"
//bright cyan background
#define CYAN_BG_B ";106"
//white background
#define WHITE_BG ";107"
#define DASH ";9"
#define BOLD ";1"
#define ITALICS ";3"
#define STRIKETHROUGH ";9"
// I can't explain this one
#define REVERSE ";7"
#define CLEAR ";0"
// underline light
#define UNDERLINE_L ";4"
// underline heavy
#define UNDERLINE_H ";21"
/**
* creates a format string based on input parameters.
* #param argc the number of ansi flags to use
* #param buffer the buffer to write the format string into. A size of 64 should be enough for most reasonable uses
* #param ... the ansi flags to use. Examples include RED, BLUE, DASH, etc
* #return the same pointer buffer, for ease of use in printf functions
*/
char* format(char* buffer, int argc, ...);
<format.c>
#include "format.h"
char* format(char* buffer, int argc, ...){
buffer[0] = '\0';
va_list argv;
int i;
// calculate available memory in buffer available for ANSI codes
int buffer_len = FMT_BUF_SIZE - strlen(FMT_START) - strlen(FMT_END) - 1;
// initialize argv to get data from the variable arguments
va_start(argv, argc);
strcat(buffer, FMT_START);
/* access all the arguments assigned to valist */
for (i = 0; i < argc; i++) {
// retrieve ansi code and substract length to available length to prevent overflows
char* code = va_arg(argv, char*);
buffer_len -= strlen(code);
// concatenate ansi code if there's space available in the buffer, else break out of loop
if(buffer_len >= 0) strcat(buffer, code);
else break;
}
strcat(buffer, FMT_END);
/* clean memory reserved for valist */
va_end(argv);
return buffer;
}
The macro works as intended, and the format function will now stop reading arguments early if the format buffer capacity is exceeded (the buffer must at least be of length 4, and i believe the longest individual ansi code is 4 characters long).

Linux framebuffer not updating when using KD_GRAPHICS

I've been trying to do some old-school drawing on the Linux framebuffer. I'm aware this is not the "modern" way to do things but exploring it out of curiosity. I am confused that my screen does not seem to update as expected when using KD_GRAPHICS mode.
Running the following program from the console to paint the screen white, the framebuffer will display a section of white on the screen, then stop updating for the duration of the sleep(), then briefly flash white before returning to KD_TEXT mode. If I comment out the KDSETMODE changes, then the framebuffer is painted white immediately except for the cursor being visible (which I don't want) and stays white for the expected 5 seconds. Can someone enlighten me why I am not seeing screen updates with KD_GRAPHCIS?
Machine is running Ubuntu 20.04 with a 5.13.0-52-generic kernel.
#include <linux/fb.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/kd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <unistd.h>
uint32_t pixel_color(uint8_t r, uint8_t g, uint8_t b, struct fb_var_screeninfo *vinfo)
{
return (r<<vinfo->red.offset) | (g<<vinfo->green.offset) | (b<<vinfo->blue.offset);
}
int main()
{
ioctl(0,KDSETMODE,KD_GRAPHICS);
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;
int fb_fd = open("/dev/fb0",O_RDWR);
ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo);
ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo);
long screensize = vinfo.yres_virtual * finfo.line_length;
uint8_t *fbp = mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, (off_t)0);
int x,y;
for (x=0;x<vinfo.xres;x++)
for (y=0;y<vinfo.yres;y++)
{
long location = x * (vinfo.bits_per_pixel/8) + y * finfo.line_length;
*((uint32_t*)(fbp + location)) = pixel_color(0xFF,0xFF,0xFF, &vinfo);
}
sleep(5);
ioctl(0,KDSETMODE,KD_TEXT);
return 0;
}

Recursive audio solution

I just want to get my head wrapped around recursion using something simple. I want amplitudes from 1 to -1. This will take less than two minutes.
I just through together a recursive solution to practice recursing.
/* This program outputs amplitudes within a frequency of 440Hz.
The number of audio samples will add of up to 1024, and the highest frequency
We can generate is about 20KilHz.
*/
#include<stdio.h>
#include<math.h>
#define PI 3.141597
#define TwoPI (2*3.141597)
#define SampleRate 441000
#define Frequency 440
#define Buffer 1024
/* Recursively Generates Signal, it is called in main. */
/*int CalculateSignal(int Sampler){
Sampler=0;
int Sample= 0;
Sample=sin(Frequency*TwoPI*SampleRate);
printf("%.6f",Sample);
if(Sampler==Buffer){
return Sample;
}
else if(Sampler<Buffer){
return CalculateSignal(Sampler+1);
}
}*/
int main(){
CalculateSignal(1024);
}

uC/OS hardware register read

I am trying to use the low power timer on the Freescale FRDM K64F, with IAR as design framework and uC/OS as operating system. I read the manual and I found that the timer is controlled by means of a bunch of registers, but I am getting in trouble with the register reading and writing.
At first I am just trying to read a register with the code attached below. The code stucks as soon as the read line is reached: the line “after read” is not printed on terminal. Searching through the debugger, I found out that the hardfault_handler exception is raised. I am not a software expert, so: does anyone have an idea about what is the problem here? It should be something related with the OS, but I can’t understand what. I said I am not a software expert, so, should I have forgot to tell something important, please let me know. Thanks in advance.
#include "fsl_interrupt_manager.h"
#include <math.h>
#include <lib_math.h>
#include <cpu_core.h>
#include <app_cfg.h>
#include <os.h>
#include <fsl_os_abstraction.h>
#include <system_MK64F12.h>
#include <board.h>
#include <bsp_ser.h>
/*
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
static OS_TCB AppTaskStartTCB;
static CPU_STK AppTaskStartStk[APP_CFG_TASK_START_STK_SIZE];
/*
*********************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static void AppTaskStart (void *p_arg);
int main (void)
{
OS_ERR err;
#if (CPU_CFG_NAME_EN == DEF_ENABLED)
CPU_ERR cpu_err;
#endif
hardware_init();
GPIO_DRV_Init(switchPins, ledPins);
#if (CPU_CFG_NAME_EN == DEF_ENABLED)
CPU_NameSet((CPU_CHAR *)"MK64FN1M0VMD12",
(CPU_ERR *)&cpu_err);
#endif
OSA_Init(); /* Init uC/OS-III. */
OSTaskCreate(&AppTaskStartTCB, /* Create the start task */
"App Task Start",
AppTaskStart,
0u,
APP_CFG_TASK_START_PRIO,
&AppTaskStartStk[0u],
(APP_CFG_TASK_START_STK_SIZE / 10u),
APP_CFG_TASK_START_STK_SIZE,
0u,
0u,
0u,
(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_SAVE_FP),
&err);
OSA_Start(); /* Start multitasking (i.e. give control to uC/OS-III). */
while (DEF_ON) { /* Should Never Get Here */
;
}
}
static void AppTaskStart (void *p_arg)
{
OS_ERR os_err;
(void)p_arg; /* See Note #1 */
char string[800];
CPU_Init(); /* Initialize the uC/CPU Services. */
Mem_Init(); /* Initialize the Memory Management Module */
Math_Init(); /* Initialize the Mathematical Module */
int * PSR = (int *) 0x42040004;
BSP_Ser_Init(115200u);
APP_TRACE_DBG(("Blinking RGB LED...\n\r"));
int value;
APP_TRACE_DBG(("Before read\n"));
value=*PSR;
APP_TRACE_DBG(("After read\n"));
APP_TRACE_DBG(("Before sprintf\n"));
sprintf(string,"Value is %d\n",value);
APP_TRACE_DBG(("After sprintf\n"));
}

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