Passing a generic argument in a C function - c

I have a struct
typedef struct
{
void *l_var;
void *r_var;
}EXPR;
EXPR expr;
I initialize it
expr.l_var = &motor_rt_params[0].position;
expr.r_var = &motor_rt_params[1].position;
Now I want to operate with the values
void SCRIPT_Process(void *l_var, void *r_var, uint32_t oper)
{
int32_t res;
switch (oper)
{
case OP_PLUS:
res = *((??? *) l_var) + *((??? *)r_var);
break;
case OP_MINUS:
res = *((??? *) l_var) - *((??? *)r_var);
break;
}
}
SCRIPT_Process(expr.l_var , expr.r_var , OP_PLUS);
The variables may be 32, 16, 8 bit.
The question - how can I cast it to an appropriate type (instead of (??? *)) in run time?

What about to also save the size when you initialize the pointers ?
typedef struct
{
void *l_var;
void *r_var;
size_t sz;
}EXPR;
expr.l_var = &motor_rt_params[0].position;
expr.r_var = &motor_rt_params[1].position;
expr.sz = sizeof(motor_rt_params[1].position);
allowing to to for instance
void SCRIPT_Process(void *l_var, void *r_var, size_t sz, uint32_t oper)
{
int32_t res;
switch (oper)
{
case OP_PLUS:
if (sz == sizeof(int32_t))
res = *((int32_t *) l_var) + *((int32_t *)r_var);
else /* suppose a int16_t */
res = *((int16_t *) l_var) + *((int16_t *)r_var);
break;
case OP_MINUS:
if (sz == sizeof(int))
res = *((int32_t *) l_var) - *((int32_t *)r_var);
else /* suppose a int16_t */
res = *((int16_t *) l_var) - *((int16_t *)r_var);
}
}
SCRIPT_Process(expr.l_var , expr.r_var , expr.sz, OP_PLUS);
supposing it is int or short only, I let you adding the case of a int8_t
The advantage also placing the size in the EXPR is to not lost that information / create inconsistencies by error because managed in different piece of code.
Or may be to give the EXPR rather than the fields separately in arguments to SCRIPT_Process ?
May be you also need to know if signed or unsigned, with an additional field, or using an int for the size valuing the positive size for unsigned (4, 2 or 1) and negative size for a signed (-4, -2 -1).
An other way is to save pointers to the right functions rather than the size, a kind of C++ virtual implementation.
Of course all of that supposes you cannot save values as int32_t in the struct and you really need to save the pointers.

safe and the same generic. No pointer punning. The type list in the union might be much longer
typedef union
{
int8_t u8;
int16_t u16;
int32_t u32;
}data_t;
int32_t get(data_t *o, int size)
{
switch (size)
{
case 8:
return o -> u8;
break;
case 16:
return o -> u16;
break;
default:
return o -> u32;
break;
}
}
void SCRIPT_Process(data_t *l_var, data_t *r_var, uint32_t oper, int sizel, int sizer)
{
int32_t res;
int32_t l = get(l_var, sizel);
int32_t r = get(r_var, sizer);
switch (oper)
{
case OP_PLUS:
res = l + r;
break;
case OP_MINUS:
res = l -r;
break;
/* ..... */
}
}

You can simply store the values in the void*, rather than their address:
expr.l_var = (void*)motor_rt_params[0].position;
expr.r_var = (void*)motor_rt_params[1].position;
Then to use them, cast to intptr_t (values, not pointers). These types are guaranteed to be the same width as a pointer, so you won't need to care anymore about the width of the source values.

The variables may be 32, 16, 8 bit.
So you need to know:
variables addresses
variables types and
operation
And you need to pass that information along.
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
enum type_e {
TYPE_u8,
TYPE_u16,
TYPE_u32,
// TODO: add more, ex. TYPE_INT, TYPE_DOUBLE, etc.
};
enum oper_e {
OP_PLUS,
OP_MINUS,
// TODO: add mode, ex. OP_POW or OP_DIV etc.
};
void SCRIPT_Process(void *res, const void *l, const void *r, enum type_e type, enum oper_e oper)
{
switch (oper) {
case OP_PLUS:
switch (type) {
case TYPE_u8:
*(uint8_t*)res = *(uint8_t*)l + *(uint8_t*)r;
break;
case TYPE_u16:
*(uint16_t*)res = *(uint16_t*)l + *(uint16_t*)r;
break;
case TYPE_u32:
*(uint32_t*)res = *(uint32_t*)l + *(uint32_t*)r;
break;
default:
assert(0);
}
break;
case OP_MINUS:
// TODO:
assert(0);
}
}
int main() {
uint32_t l = 5, r = 2, res;
SCRIPT_Process(&res, &r, &l, TYPE_u32, OP_PLUS);
printf("%d + %d = %d\n", (int)l, (int)r, (int)res);
}
Would be nice to provide a macro to make the code more verbose and with less typing:
#define SCRIPT_PROCESS_MACRO(type, res, l, op, r) \
*(type*)res = *(type*)l op *(type*)r;
void SCRIPT_Process(void *res, void *l, void *r, enum type_e type, enum oper_e oper)
{
switch (oper) {
case OP_PLUS:
switch (type) {
case TYPE_u8:
SCRIPT_PROCESS_MACRO(uint8_t, res, l, +, r);
break;
case TYPE_u16:
SCRIPT_PROCESS_MACRO(uint16_t, res, l, +, r);
break;
case TYPE_u32:
SCRIPT_PROCESS_MACRO(uint32_t, res, l, +, r);
break;
default:
assert(0);
}
break;
case OP_MINUS:
// TODO:
assert(0);
}
}
Or even more simple with more macros, which makes adding new operations and types trivial:
#define SCRIPT_PROCESS_MACRO(type, res, l, op, r) \
*(type*)res = *(type*)l op *(type*)r;
#define SCRIPT_PROCESS_TYPE_CASES(type, res, l, op, r) \
switch (type) { \
case TYPE_u8: SCRIPT_PROCESS_MACRO(uint8_t, res, l, op, r); break; \
case TYPE_u16: SCRIPT_PROCESS_MACRO(uint16_t, res, l, op, r); break; \
case TYPE_u32: SCRIPT_PROCESS_MACRO(uint32_t, res, l, op, r); break; \
default: assert(0); break; \
}
void SCRIPT_Process(void *res, void *l, void *r, enum type_e type, enum oper_e oper)
{
switch (oper) {
case OP_PLUS:
SCRIPT_PROCESS_TYPE_CASES(type, res, l, +, r);
break;
case OP_MINUS:
SCRIPT_PROCESS_TYPE_CASES(type, res, l, -, r);
break;
}
}
Or you could even go with even more generic solution, having separate types for result, left and right operands:
void SCRIPT_Process(
void *res, enum type_e restype,
const void *l, enum type_e ltype,
const void *r, enum type_e rtype,
enum oper_e oper) {
if (restype == TYPE_u8 && ltype == TYPE_u32 && rtype == TYPE_u16 && oper == OP_ADD) {
*(uint8_t*)res = *(uint32_t*)ltype + *(uint16_t*)rtype;
} if ( // and so on so on so on so on ....
}

Related

Mex type C file to normal C file

I have a c file written using mex functions, run by Matlab using mex compiler Now I want to run those mex type .c files without Matlab environment. So, is there any way to convert mex type .c file to normal .c file ? Or any other options available for the above mentioned concern.
Thanks in advance....!
Here is the one mex type .c file for sample.
#include <stdlib.h>
#include "mex.h"
#include "matrix.h"
#include "distances.h"
#include "ltl2tree.h"
#include "param.h"
#define BUFF_LEN 4096
char * emalloc(size_t n)
{
char *tmp;
if (!(tmp = (char *) mxMalloc(n)))
mexErrMsgTxt("mx_dp_taliro: not enough memory!");
memset(tmp, 0, n);
return tmp;
}
int tl_Getchar(int *cnt, size_t hasuform, char *uform)
{
if (*cnt < hasuform)
return uform[(*cnt)++];
(*cnt)++;
return -1;
}
void tl_UnGetchar(int *cnt)
{
if (*cnt > 0) (*cnt)--;
}
#define Binop(a) \
fprintf(miscell->tl_out, "("); \
dump(n->lft, miscell); \
fprintf(miscell->tl_out, a); \
dump(n->rgt, miscell); \
fprintf(miscell->tl_out, ")")
static void sdump(Node *n, char *dumpbuf)
{
switch (n->ntyp) {
case PREDICATE: strcat(dumpbuf, n->sym->name);
break;
case U_OPER: strcat(dumpbuf, "U");
goto common2;
case V_OPER: strcat(dumpbuf, "V");
goto common2;
case OR: strcat(dumpbuf, "|");
goto common2;
case AND: strcat(dumpbuf, "&");
common2: sdump(n->rgt,dumpbuf);
common1: sdump(n->lft,dumpbuf);
break;
case NEXT: strcat(dumpbuf, "X");
goto common1;
case NOT: strcat(dumpbuf, "!");
goto common1;
case TRUE: strcat(dumpbuf, "T");
break;
case FALSE: strcat(dumpbuf, "F");
break;
default: strcat(dumpbuf, "?");
break;
}
}
Symbol *DoDump(Node *n, char *dumpbuf, Miscellaneous *miscell)
{
if (!n) return ZS;
if (n->ntyp == PREDICATE)
return n->sym;
dumpbuf[0] = '\0';
sdump(n,dumpbuf);
return tl_lookup(dumpbuf, miscell);
}
void dump(Node *n, Miscellaneous *miscell)
{
if (!n) return;
switch(n->ntyp) {
case OR: Binop(" || "); break;
case AND: Binop(" && "); break;
case U_OPER: Binop(" U "); break;
case V_OPER: Binop(" V "); break;
case NEXT:
fprintf(miscell->tl_out, "X");
fprintf(miscell->tl_out, " (");
dump(n->lft, miscell);
fprintf(miscell->tl_out, ")");
break;
case NOT:
fprintf(miscell->tl_out, "!");
fprintf(miscell->tl_out, " (");
dump(n->lft, miscell);
fprintf(miscell->tl_out, ")");
break;
case FALSE:
fprintf(miscell->tl_out, "false");
break;
case TRUE:
fprintf(miscell->tl_out, "true");
break;
case PREDICATE:
fprintf(miscell->tl_out, "(%s)", n->sym->name);
break;
case -1:
fprintf(miscell->tl_out, " D ");
break;
default:
printf("Unknown token: ");
tl_explain(n->ntyp);
break;
}
}
void tl_explain(int n)
{
switch (n) {
case ALWAYS: printf("[]"); break;
case EVENTUALLY: printf("<>"); break;
case IMPLIES: printf("->"); break;
case EQUIV: printf("<->"); break;
case PREDICATE: printf("predicate"); break;
case OR: printf("||"); break;
case AND: printf("&&"); break;
case NOT: printf("!"); break;
case U_OPER: printf("U"); break;
case V_OPER: printf("V"); break;
case NEXT: printf("X"); break;
case TRUE: printf("true"); break;
case FALSE: printf("false"); break;
case ';': printf("end of formula"); break;
default: printf("%c", n); break;
}
}
static void non_fatal(char *s1, char *s2, int *cnt, char *uform, int *tl_yychar, Miscellaneous *miscell)
{
int i;
printf("TaLiRo: ");
if (s2)
printf(s1, s2);
else
printf(s1);
if ((*tl_yychar) != -1 && (*tl_yychar) != 0)
{ printf(", saw '");
tl_explain((*tl_yychar));
printf("'");
}
printf("\nTaLiRo: %s\n---------", uform);
for (i = 0; i < (*cnt); i++)
printf("-");
printf("^\n");
fflush(stdout);
(miscell->tl_errs)++;
}
void
tl_yyerror(char *s1, int *cnt, char *uform, int *tl_yychar, Miscellaneous *miscell)
{
Fatal(s1, (char *) 0, cnt, uform, tl_yychar, miscell);
}
void
Fatal(char *s1, char *s2, int *cnt, char *uform, int *tl_yychar, Miscellaneous *miscell)
{
non_fatal(s1, s2, cnt, uform, tl_yychar, miscell);
tl_exit(0);
}
void fatal(char *s1, char *s2, int *cnt, char *uform, int *tl_yychar, Miscellaneous *miscell)
{
non_fatal(s1, s2, cnt, uform, tl_yychar, miscell);
tl_exit(0);
}
void put_uform(char *uform, Miscellaneous *miscell)
{
fprintf(miscell->tl_out, "%s", uform);
}
void tl_exit(int i)
{
mexErrMsgTxt("mx_dp_taliro: unexpected error, tl_exit executed.");
}
void mexFunction(int nlhs, mxArray *plhs[],int nrhs, const mxArray *prhs[])
{
/* Variables needed to process the input */
int status, pstatus;
mwSize buflen, pbuflen;
size_t NElems,NCells;
mwSize ndimA, ndimb, ndimG, ndim, pdim,ndimP;
const mwSize *dimsA, *dimsb, *dimsG, *dims, *pardims,*dimsP;
mwIndex jstruct, iii, jjj, i1, j1, idx_j;
mxArray *tmp,*tmp1,*tmp_cell;
/* Variables needed for monitor */
Node *node;
double *XTrace, *TStamps, *LTrace;
DistCompData distData;
PMap *predMap;
int ii,jj,kk,ll,objs,tempI;
bool par_on;
bool is_Multi_HA;
bool initial_of_par;
int npred, npar;
static char uform[BUFF_LEN];
static size_t hasuform=0;
static int *cnt;
int temp = 0;
Miscellaneous *miscell = (Miscellaneous *) emalloc(sizeof(Miscellaneous));
int *tl_yychar = (int *) emalloc(sizeof(int));
miscell->dp_taliro_param.LTL = 1;
miscell->dp_taliro_param.ConOnSamples = 0;
miscell->dp_taliro_param.SysDim = 0;
miscell->dp_taliro_param.nSamp = 0;
miscell->dp_taliro_param.nPred = 0;
miscell->dp_taliro_param.true_nPred = 0;
miscell->dp_taliro_param.tnLoc = 0;
miscell->dp_taliro_param.nInp = 0;
miscell->dp_taliro_param.nCLG = 0;
miscell->tl_errs = 0;
miscell->type_temp = 0;
/* Reset cnt to 0:
cnt is the counter that points to the next symbol in the formula
to be processed. This is a static variable and it retains its
value between Matlab calls to mx_dp_taliro. */
cnt = &temp;
/* Other initializations */
miscell->dp_taliro_param.nInp = nrhs;
par_on = false;
initial_of_par = false;
npred = 0;
npar= 0;
/* Make sure the I/O are in the right form */
if(nrhs < 3)
mexErrMsgTxt("mx_dp_taliro: At least 3 inputs are required.");
else if(nlhs > 1)
mexErrMsgTxt("mx_dp_taliro: Too many output arguments.");
else if(nrhs > 8)
mexErrMsgTxt("mx_dp_taliro: Too many input arguments.");
else if(!mxIsChar(prhs[0]))
mexErrMsgTxt("mx_dp_taliro: 1st input must be a string with TL formula.");
else if(!mxIsStruct(prhs[1]))
mexErrMsgTxt("mx_dp_taliro: 2nd input must be a structure (predicate map).");
else if(!mxIsDouble(prhs[2]))
mexErrMsgTxt("mx_dp_taliro: 3rd input must be a numerical array (State trace).");
else if(nrhs>3 && !mxIsDouble(prhs[3]))
mexErrMsgTxt("mx_dp_taliro: 4th input must be a numerical array (Time stamps).");
else if(nrhs>5 && !mxIsDouble(prhs[4]))
mexErrMsgTxt("mx_dp_taliro: 5th input must be a numerical array (Location trace).");
else if(nrhs>5 && !(mxIsDouble(prhs[5])||mxIsCell(prhs[5])))
mexErrMsgTxt("mx_dp_taliro: 6th input must be a numerical array \n (Minimum path distance to each control location for each predicate).");
else if(nrhs>7 && !(mxIsCell(prhs[6])))
mexErrMsgTxt("mx_dp_taliro: 7th input must be a cell array (Adjacency list).");
else if(nrhs>7 && !(mxIsStruct(prhs[7])||mxIsCell(prhs[7])))
mexErrMsgTxt("mx_dp_taliro: 8th input must be a structure (guard map).");
if(nlhs > 1)
mexErrMsgTxt("Too many output arguments.");
plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL);
/* Process inputs */
/* Get the formula */
ndim = mxGetNumberOfDimensions(prhs[0]);
dims = mxGetDimensions(prhs[0]);
buflen = dims[1]*sizeof(mxChar)+1;
if (buflen >= BUFF_LEN)
{
mexPrintf("%s%d%s\n", "The formula must be less than ", BUFF_LEN," characters.");
mexErrMsgTxt("mx_dp_taliro: Formula too long.");
}
status = mxGetString(prhs[0], uform, buflen);
hasuform = strlen(uform);
for (iii=0; iii<hasuform; iii++)
{
if (uform[iii] == '\t' || uform[iii] == '\"' || uform[iii] == '\n')
uform[iii] = ' ';
}
/* Get state trace */
ndim = mxGetNumberOfDimensions(prhs[2]);
if (ndim>2)
mexErrMsgTxt("mx_dp_taliro: The state trace is not in proper form!");
dims = mxGetDimensions(prhs[2]);
miscell->dp_taliro_param.nSamp = dims[0];
miscell->dp_taliro_param.SysDim = dims[1];
XTrace = mxGetPr(prhs[2]);
/* Get time stamps */
if (nrhs>3)
{
ndim = mxGetNumberOfDimensions(prhs[3]);
if (ndim>2)
mexErrMsgTxt("mx_dp_taliro: The time stamp sequence is not in proper form!");
dims = mxGetDimensions(prhs[3]);
if (miscell->dp_taliro_param.nSamp != dims[0])
mexErrMsgTxt("mx_dp_taliro: The lengths of the time stamp sequence and the state trace do not match!");
TStamps = mxGetPr(prhs[3]);
}
/* Get location trace and location graph */
if (nrhs>4)
{
ndim = mxGetNumberOfDimensions(prhs[4]);
if (ndim>2)
mexErrMsgTxt("mx_dp_taliro: The location trace is not in proper form!");
dims = mxGetDimensions(prhs[4]);
if (miscell->dp_taliro_param.nSamp != dims[0])
mexErrMsgTxt("mx_dp_taliro: The lengths of the location trace and the state trace do not match!");
LTrace = mxGetPr(prhs[4]);
/* For Multiple H.A.s is added */
if(nrhs>5 && (mxIsCell(prhs[5]))){/* For Multiple H.A.s */
is_Multi_HA=1;
miscell->dp_taliro_param.nCLG = dims[1];
ndim = mxGetNumberOfDimensions(prhs[5]);
if (ndim>2)
{
mexErrMsgTxt("mx_dp_taliro.c: !");
}
dims = mxGetDimensions(prhs[5]);
if (dims[0]!=1)
{
mexErrMsgTxt("mx_dp_taliro: DMin is a cell, it must be a column vector cell!");
}
if (dims[1]!=miscell->dp_taliro_param.nCLG)
{
mexErrMsgTxt("mx_dp_taliro: DMin is a cell, it must be of the size of nCLG!");
}
distData.LDistNCLG = (double **)emalloc((miscell->dp_taliro_param.nCLG)*sizeof(double*));
miscell->dp_taliro_param.tnLocNCLG = (mwSize*)emalloc((miscell->dp_taliro_param.nCLG)*sizeof(mwSize));
for(kk=0; kk<miscell->dp_taliro_param.nCLG; kk++){
tmp = mxGetCell(prhs[5],kk);
dims = mxGetDimensions(tmp);
miscell->dp_taliro_param.tnLocNCLG[kk] = dims[0];
distData.LDistNCLG[kk]=mxGetPr(tmp);
}
}
else if(nrhs>5) {/* For Single H.A. */
is_Multi_HA=0;
ndim = mxGetNumberOfDimensions(prhs[5]);
if (ndim>2)
mexErrMsgTxt("mx_dp_taliro: The minimum distance array is not in proper form!");
dims = mxGetDimensions(prhs[5]);
miscell->dp_taliro_param.tnLoc = dims[0];
distData.LDist = mxGetPr(prhs[5]);
}
}
}
In your code, you are using a lot of functions and data types, provided by the MEX API. Therefore, if you want to produce "normal" C-Code you will have to completly refactor your code.
But one possible solution might be to compile your source code outside MATLAB IDE, by gcc or any other compiler. In this case you will have to include all necessary header files and link against all libraries you need.
Follwo the link to find a pretty good documentation from Mathworks:
https://de.mathworks.com/matlabcentral/answers/377799-compiling-mex-files-without-the-mex-command
Another way for creating C Code from Matlab Scipts/Functions is to use MATLAB Coder:
https://de.mathworks.com/products/matlab-coder.html
Hope this is helpful.

adding two structs representing different types together

I have a struct like so:
typedef enum any_type{
ANY_TYPE_CHAR,
ANY_TYPE_UCHAR,
ANY_TYPE_SHORT,
ANY_TYPE_USHORT,
ANY_TYPE_INT,
ANY_TYPE_UINT,
ANY_TYPE_LONG,
ANY_TYPE_ULONG,
ANY_TYPE_FLOAT,
ANY_TYPE_DOUBLE,
} any_type;
typedef struct any{
any_type type;
union{
char as_char;
unsigned char as_uchar;
short as_short;
unsigned short as_ushort;
int as_int;
unsigned int as_uint;
long as_long;
unsigned long as_ulong;
float as_float;
double as_double;
};
} any;
which represents a variable which is one of the types specified. There are four possible operations on these structs, which are addition, subtraction, multiplication and division.
My question is, is there an efficient way to do these operations on them without doing lots of if statements for every case? That would result in 4 * 10 * 10 = 400 if/else if statements for these four operations alone, which is certainly not efficient!
My code is in C
Thanks in advance.
To avoid a combinational nightmare, consider that there are 3 groups of types, unsigned integers, signed integers, floating point.
A goal is to determine which of the 3 groups the operation is to use. Then get the value of the operand per its type and the target group. Do the math per that group. Then save per the group and the highest ranking type.
For each of the 10 types, create 10 functions to fetch the data and return as the widest unsigned integer. Another 10 for signed integer and lastly 10 for floating point.
For each for the 10 types, do the same to create functions to set the data. So far 60 small functions.
To access the correct function, use a table look up in a function. 3 more functions for getting, 3 more for setting.
An example addition function is at the end. It looks for the highest ranking type and gets/adds/sets based on the one of 3 groups.
Now add 3 more functions for -,/,*. Total about 60 + 6 + 4 functions.
Bonus: To add a new function like %, only takes 1 more function to write.
#include<assert.h>
typedef enum any_type{
// Insure these are in rank order
// ANY_TYPE_CHAR left out for now
ANY_TYPE_SCHAR,
ANY_TYPE_UCHAR,
ANY_TYPE_SHORT,
ANY_TYPE_USHORT,
ANY_TYPE_INT,
ANY_TYPE_UINT,
ANY_TYPE_LONG,
ANY_TYPE_ULONG,
ANY_TYPE_FLOAT,
ANY_TYPE_DOUBLE,
ANY_TYPE_N,
} any_type;
typedef struct any{
any_type type;
union{
signed char as_schar;
unsigned char as_uchar;
short as_short;
unsigned short as_ushort;
int as_int;
unsigned int as_uint;
long as_long;
unsigned long as_ulong;
float as_float;
double as_double;
};
} any;
/////////////////////////////////////
unsigned long any_uget_schar(const any *x) {
return (unsigned long) x->as_schar;
}
unsigned long any_uget_uchar(const any *x) {
return x->as_uchar;
}
/* 8 more */
unsigned long any_get_unsigned(const any *x) {
static unsigned long (*uget[ANY_TYPE_N])(const any *x) = {
any_uget_schar, any_uget_uchar, /* 8 others */ };
assert(x->type < ANY_TYPE_N);
return (uget[x->type])(x);
}
/////////////////////////////////////
signed long any_sget_schar(const any *x) {
return x->as_schar;
}
signed long any_sget_uchar(const any *x) {
return x->as_uchar;
}
/* 8 more */
signed long any_get_signed(const any *x) {
static signed long (*sget[ANY_TYPE_N])(const any *x) = {
any_sget_schar, any_sget_uchar, /* 8 others */ };
assert(x->type < ANY_TYPE_N);
return sget[x->type](x);
}
/////////////////////////////////////
double any_get_fp(const any *x); // similar for floating point
/////////////////////////////////////
void any_uset_schar(any *x, unsigned long y) {
x->as_schar = (signed char) y;
}
void any_uset_uchar(any *x, unsigned long y) {
x->as_uchar = (unsigned char) y;
}
/* 8 more */
void any_set_unsigned(any *x, unsigned long y) {
static void (*uset[ANY_TYPE_N])(any *x, unsigned long y) = {
any_uset_schar, any_uset_uchar, /* 8 others */ };
assert(x->type < ANY_TYPE_N);
uset[x->type](x,y);
}
/* 10 more for any_sset_... */
/* 10 more for any_fset_... */
///////////////////////////////////////////////
static const char classify[] = "susususuff";
any any_get_add(any a, any b) {
any sum;
sum.type = max(a.type, b.type);
assert(sum.type < ANY_TYPE_N);
switch (classify[sum.type]) {
case 's':
any_set_signed(&sum, any_get_signed(&a) + any_get_signed(&b));
break;
case 'u':
any_set_unsigned(&sum, any_get_unsigned(&a) + any_get_unsigned(&b));
break;
case 'f':
any_set_fp(&sum, any_get_signed(&a) + any_get_signed(&b));
break;
default:
assert(0);
}
return sum;
}
This is going to require a lot of code, because combinatorial explosion is part of the problem.
Use a table-driven approach: define two tables, one for converting between types, and one for performing operations on them.
For conversion make a function pointer type
typedef any (*convert_any)(any val, any_type target);
and make a table of conversions keyed on any_type:
convert_any conversion[] = {
convert_char,
convert_uchar,
convert_short,
...
};
with implementations like this for each type (you'll need ten of these):
static any convert_char(any val, any_type target) {
any res = { .type = target };
switch (target) {
case ANY_TYPE_CHAR: res.as_char = val.as_char; break;
case ANY_TYPE_INT: res.as_int = (int)val.as_char; break;
...
}
return res;
}
For operations make another function pointer type:
typedef any (*operation_any)(any left, any right);
You will need to make a 3D array of such pointers - one for each triple of operation, the type of its left operand, and the type of its right operand.
Implementations would look like this:
static any add_int(any left, any right) {
any lhs = conversion[left.type](left, ANY_TYPE_INT);
any rhs = conversion[right.type](right, ANY_TYPE_INT);
any res {.type = ANY_TYPE_INT, .as_int = lhs.as_int + rhs.as_int};
return res;
}
static any add_double(any left, any right) {
any lhs = conversion[left.type](left, ANY_TYPE_DOUBLE);
any rhs = conversion[right.type](right, ANY_TYPE_DOUBLE);
any res {.type = ANY_TYPE_DOUBLE, .as_double = lhs.as_double + rhs.as_double };
return res;
}
There will be forty such implementations - one for each result type and operation pair. The 3D table will contain 400 entries, depending on the type of the result the operation needs to produce. The call will look up into 3D array, find operation_any pointer, pass two arguments, and get a result like this:
any res = operations[PLUS_OP][left.type][right.type](left, right);
You should make expr structure. This mean one node of AST (Abstruct Syntax Tree). And it have op. This mean +,-,*,/ for the lhs, rhs. do_expr caluculate expression. Finally, dump_any print the value.
#include <stdio.h>
#include <assert.h>
typedef enum any_type{
ANY_TYPE_CHAR,
ANY_TYPE_UCHAR,
ANY_TYPE_SHORT,
ANY_TYPE_USHORT,
ANY_TYPE_INT,
ANY_TYPE_UINT,
ANY_TYPE_LONG,
ANY_TYPE_ULONG,
ANY_TYPE_FLOAT,
ANY_TYPE_DOUBLE,
} any_type;
typedef struct any{
any_type type;
union{
char as_char;
unsigned char as_uchar;
short as_short;
unsigned short as_ushort;
int as_int;
unsigned int as_uint;
long as_long;
unsigned long as_ulong;
float as_float;
double as_double;
};
} any;
typedef enum op_type{
OP_PLUS,
OP_MINUS,
OP_MULT,
OP_DIVID,
} op_type;
typedef struct {
int op;
any lhs;
any rhs;
} expr;
int
do_expr(expr* e, any *r) {
switch (e->op) {
case OP_PLUS:
r->type = e->lhs.type;
r->as_int = e->lhs.as_int + e->rhs.as_int;
return 0;
break;
default:
fprintf(stderr, "unknown operation\n");
break;
}
return 1;
}
void
dump_any(any* a) {
switch (a->type) {
case ANY_TYPE_INT:
printf("%d\n", a->as_int);
break;
default:
assert(!"unknown type");
break;
}
}
int
main(int argc, char* argv[]) {
expr e1 = {
.op = OP_PLUS,
.lhs = { .type = ANY_TYPE_INT, as_int: 1, },
.rhs = { .type = ANY_TYPE_INT, as_int: 2, },
};
any ret;
if (do_expr(&e1, &ret) == 0) {
dump_any(&ret);
}
return 0;
}
Sorry, but you have to do case switching since the compiler has to generate different CPU instructions for different types. The kind of operation is defined by the choice of the variable.

Refactoring: Very similar switch cases

I have several struct declared which contain different data. I also have an enum that corresponds to those structures. There are several places in my code where I need to access information about the structures and I'm doing it via the enum. This results in few switch statements that return this information.
I've enclosed those switch statements in their own functions in order to re-use wherever possible. This resulted in three functions that look very similar.
Example psuedo-code:
#include <stdio.h>
typedef struct
{
int varA;
char varB;
} A;
typedef struct
{
int varA;
int varB;
int varC;
} B;
typedef struct
{
int varA;
short varB;
} C;
typedef enum { structA, structB, structC } STRUCT_ENUM;
int returnSize(STRUCT_ENUM structType)
{
int retVal = 0;
switch(structType)
{
case structA:
retVal = sizeof(A);
break;
case structB:
retVal = sizeof(B);
break;
case structC:
retVal = sizeof(C);
break;
default:
break;
}
return retVal;
}
void printStructName(STRUCT_ENUM structType)
{
switch(structType)
{
case structA:
printf("Struct: A\r\n");
break;
case structB:
printf("Struct: B\r\n");
break;
case structC:
printf("Struct: C\r\n");
break;
default:
break;
}
}
void createDataString(STRUCT_ENUM structType, char* output, unsigned char* input)
{
switch(structType)
{
case structA:
{
A a = *(A*)input;
sprintf(output, "data: %d, %d", a.varA, a.varB);
break;
}
case structB:
{
B b = *(B*)input;
sprintf(output, "data: %d, %d, %d", b.varA, b.varB, b.varC);
break;
}
case structC:
{
C c = *(C*)input;
sprintf(output, "data: %d, %d", c.varA, c.varB);
break;
}
default:
break;
}
}
int main(void) {
char foobar[50];
printf("Return size: %d\r\n", returnSize(structA));
printStructName(structB);
C c = { 10, 20 };
createDataString(structC, foobar, (unsigned char*) &c);
printf("Data string: %s\r\n", foobar);
return 0;
}
Those free functions basically contain the same switch with different code placed in the cases. With this setup, adding new struct and enum value results in three places in the code that needs changing.
The question is: is there a way to refactor this into something more maintainable? Additional constraint is that the code is written in C.
EDIT: online example: http://ideone.com/xhXmXu
You can always use static arrays and use STRUCT_ENUM as the index. Given the nature of your functions, I don't really know if you would consider it more maintainable, but it's an alternative I usually prefer, examples for names and sizes:
typedef enum { structA, structB, structC, STRUCT_ENUM_MAX } STRUCT_ENUM;
char *struct_name[STRUCT_ENUM_MAX] = {[structA] = "Struct A", [structB] = "Struct B", [structC] = "Struct C"};
size_t struct_size[STRUCT_ENUM_MAX] = {[structA] = sizeof(A), [structB] = sizeof(B), [structC] = sizeof(C)};
for printing content you can keep a similar array of functions receiving a void * that will print the value of this argument.
Edit:
Added designated initializers as per Jen Gustedt's comment.
You can make it into a single function and a single switch, with an additional parameter. Like so
int enumInfo(STRUCT_ENUM structType, int type) // 1 = returnSize 2 = printStructName
{
int retVal = 0;
switch(structType)
{
case structA:
If ( type == 1 ) { retVal = sizeof(A); }
else { printf("Struct: A"); }
break;
case structB:
If ( type == 1 ) { retVal = sizeof(B); }
else { printf("Struct: B"); }
break;
case structC:
If ( type == 1 ) { retVal = sizeof(C); }
else { printf("Struct: C"); }
break;
default:
break;
}
return retVal;
}

c function pointer pass parameter during runtime

I have a pretty complex problem about c function pointers and passing the parameters to them.
I have a function pointer and a couple of function addresses within a lookup table. I get all of my data via a serial interface. First the number of the function which has to be called. I look it up in the table and pass the reference to my function pointer.
After that, i receive several pairs of 4 byte values as data as the arguments. Problem is, i have to call different functions with the same return type but a different amount of parameters.
Is there a way to pass dynamically data to a function call. Maybe by pushing them on the stack manually? Couldn't find a solution for that.
Does anybody has any idea or a hint to solve that problem?
I don't believe there's an easy way to answer this since argument passing is ABI (Application Binary Interface) specific. If your platform is fixed and you don't mind writing non-portable code then you can write your code in an ABI specific way but I wouldn't advise that.
I've solved this issue in a small emulator I wrote, for me it was easier since the number of commands was never greater than 4, I basically used a union of function pointers which had the number of arguments I wanted and a switch statement to select the right one.
typedef struct
{
union
__attribute__((__packed__))
{
void *func;
void (*func_a0)(void);
uint32_t (*func_a0r)(void);
void (*func_a1)(uint32_t);
uint32_t (*func_a1r)(uint32_t);
void (*func_a2)(uint32_t, uint32_t);
uint32_t (*func_a2r)(uint32_t, uint32_t);
void (*func_a3)(uint32_t, uint32_t, uint32_t);
uint32_t (*func_a3r)(uint32_t, uint32_t, uint32_t);
void (*func_a4)(uint32_t, uint32_t, uint32_t, uint32_t);
uint32_t (*func_a4r)(uint32_t, uint32_t, uint32_t, uint32_t);
};
unsigned args;
bool ret;
const char* name;
} jump_entry_t;
bool jump_table_exec(
jump_table_t* table, void* addr,
uint32_t* args, uint32_t* ret)
{
#ifdef JUMP_TABLE_DEBUG
if (!table)
return false;
#endif
if ((uintptr_t)addr < (uintptr_t)table->base)
return false;
unsigned i = ((uintptr_t)addr - (uintptr_t)table->base);
if ((i & 4) || (i >= table->size))
return false;
jump_entry_t j = table->entry[i >> 3];
if (!j.func)
return false;
if (j.args && !args)
return false;
if (j.ret)
{
if (!ret) return false;
switch (j.args)
{
case 0:
*ret = j.func_a0r();
break;
case 1:
*ret = j.func_a1r(args[0]);
break;
case 2:
*ret = j.func_a2r(args[0], args[1]);
break;
case 3:
*ret = j.func_a3r(args[0], args[1], args[2]);
break;
case 4:
*ret = j.func_a4r(args[0], args[1], args[2], args[3]);
break;
default:
return false;
}
}
else
{
switch (j.args)
{
case 0:
j.func_a0();
break;
case 1:
j.func_a1(args[0]);
break;
case 2:
j.func_a2(args[0], args[1]);
break;
case 3:
j.func_a3(args[0], args[1], args[2]);
break;
case 4:
j.func_a4(args[0], args[1], args[2], args[3]);
break;
default:
return false;
}
}
#ifdef JUMP_TABLE_DEBUG
if (j.name)
{
fprintf(stderr, "Info: Jump table %s(", j.name);
if (j.args >= 1) fprintf(stderr, "%" PRIu32, args[0]);
if (j.args >= 2) fprintf(stderr, ", %" PRIu32, args[1]);
if (j.args >= 3) fprintf(stderr, ", %" PRIu32, args[2]);
if (j.args >= 4) fprintf(stderr, ", %" PRIu32, args[3]);
fprintf(stderr, ")");
if (j.ret) fprintf(stderr, " returned %" PRIu32, *ret);
fprintf(stderr, ".\n");
}
#endif
return true;
}
Sometimes the following approach using unions is useful:
union foo {
struct {
int arg1;
} f1_args;
struct {
int arg1, arg2;
} f2_args;
};
int f1(union foo*);
int f2(union foo*);
int (*table[])(union foo*) = {f1, f2};
//...
union foo data;
//...
int answer = table[1](&data); // calls f2, which uses arg1 and arg2
And, if you prefer, f1 and f2 can be simple wrappers to the "real" functions, as in:
int f1(union foo *u) { return f1_real(u->f1_args.arg1); }
int f2(union foo *u) { return f2_real(u->f2_args.arg1, u->f2_args.arg2); }
This is quite flexible. But if your arguments are always only 4-byte ints, then you can get rid of the union and just use arrays. Rewritten, the above becomes:
int f1(uint32_t *a) { return f1_real(a[0]); } // wrapper
int f2(uint32_t *a) { return f2_real(a[0], a[1]); } // wrapper
int (*table[])(uint32_t *) = {f1, f2}; // lookup table
//...
uint32_t data[99]; // data from e.g. serial port
//...
int answer = table[1](data); // calls f2, which uses two args
Since the functions can distinguish their parameters, you can always give them a ... type. For example:
int f(...)
{
/* extract one int */
}
int g(...)
{
/* extract two floats */
}
...
int (*fp)(...);
if (type_one)
fp(10);
else if (type_two)
fp(1.3, 4.3);
Or better yet use a union. However, in your particular case, since the parameters themselves are "pairs of 4 bytes", you can always use an array:
struct arg
{
uint32_t pair_of_4_bytes[2];
};
int f(struct arg *args, size_t count)
{
}
int g(struct arg *args, size_t count)
{
}
...
int (*fp)(struct arg *args, size_t count);
struct arg args[MAX];
size_t count = 0;
/* get args from serial and put in args/count */
fp(args, count);
I think "Variadic functions" can solve your problem needs. Checkout a simple example here:
http://www.gnu.org/software/libc/manual/html_node/Variadic-Example.html#Variadic-Example

How can I do it smarter?

I must read and extract some values from string.
These values are coded like this:
k="11,3,1" v="140.3"
I have defined the codes and created struct with all field as well as a temp one where I store k and v. In fillFields proc I transfer values from temp struct to the right one (with the valid types).
It works but I have many fields and fillFields would need to have many if-conditions. Maybe someone could give me any hint how to write it smarter.
The simplified code now:
#define ASK "11,3,1"
#define BID "11,2,1"
#define CLOSE "3,1,1"
typedef struct tic {
float ask;
float bid;
float close;
}tic, *ticP;
typedef struct pElem {
char * k;
char * v;
}pElem, *pElemP;
void fillFields(ticP t, pElemP p)
{
if (strcmp( ASK, p->k)==0)
{
printf ("ASK %s\n", p->v);
t->ask = atof(p->v);
}
if (strcmp( BID, p->k)==0)
{
printf ("BID %s\n", p->v);
t->bid = atof(p->v);
}
if (strcmp( CLOSE, p->k)==0)
{
printf("CLOSE >>>%s<<<\n", p->v) ;
t->close = atof (p->v);
}
}
Rather than save the text value in pElem, save the converted values.
This creates an extra step in parsing k="11,3,1" v="140.3", to convert text to an enumerated type, but it's paid once. The fillFields() calls then run simpler. Assuming you have more ticP variables, it's a win.
typedef enum pElem_type {
pElem_None, pElem_ASK, pElem_BID, pElem_CLOSE, pElem_N
} pElem_type;
typedef struct pElem {
pElem_type type;
float value;
} pElem;
void fillFields(ticP t, const pElem *p) {
switch (p->type) {
case pElem_ASK:
printf("ASK %f\n", p->value);
t->ask = p->value;
break;
case pElem_BID:
printf("BID %f\n", p->value);
t->bid = p->value;
break;
case pElem_CLOSE:
printf("Close %f\n", p->value);
t->close = p->value;
break;
default:
printf("Error\n");
}
}
// Further simplifications possible
typedef struct tic {
float field[pElem_N];
}tic, *ticP;
static const char *FieldName[pElem_N] = {
"None", "ASK", "BID", "Close"
};
void fillFields(ticP t, const pElem *p) {
if (p->type < pElem_N) {
printf("%s %f\n", FieldName[p->type], p->value);
t->field[p->type] = p->value;
}
}

Resources