Related
I want to create a function that takes a array and a function and calls that function on each element in the array
I've looked around for a solution but they all seem to use macros whereas I would prefer a function if at all possible.
I'm looking for something that would work something like the following
void print_string()
{
printf("%d\n", num);
}
int array[] = { 1, 2, 3, 4, NULL }; // So the foreach function knows when it has reached the end
for_each_function(array, print_number);
Outputting:
1
2
3
4
Edit: It needs to be generic so it will probably require a macro
You should probably use function pointers. One complete implementation using function pointers is like this.
#include <stdio.h>
void for_each(int * arr, int size, void (*fun)(int))
{
for (int i = 0; i < size; i++)
{
(*fun)(arr[i]);
}
}
void print_num(int num)
{
printf("%d\n", num);
}
int main()
{
int array [] = {1, 2, 3, 4};
for_each(array, 4, print_num);
return 0;
}
Use a function pointer, like this:
#include <stdio.h>
void for_each_function(int* arr, size_t size, void(*fun)(int)) {
for(size_t i = 0; i < size; ++i)
(*fun)(arr[i]);
}
void print_number(int);
int main(void) {
int array[] = { 1, 2, 3, 4 };
for_each_function(array, 4, &print_number);
return 0;
}
void print_number(int num)
{
printf("%d\n", num);
}
Output:
1
2
3
4
Remarks:
The third parameter of for_each_function, namely void(*fun)(int),
is a function pointer named fun, with a return type void, and a
parameter of type int.
Inside the for each body, you call the function pointer like this
(*fun)(arr[i]);, which dereferences the function pointer fun, and
would pass arr[i] as its parameter. You could also use fun(arr[i]).
In the call of the for each function in main function, you need to
use the address of the function you want to point to, like this
for_each_function(array, 4, &print_number);. You could also use for_each_function(array, 4, print_number);.
PS: In your example array, I don't think you wanted to use NULL as the last element (you would probably get a warning like warning: initialization of 'int' from 'void *' makes integer from pointer without a cast [-Wint-conversion], unless NULL was defined as 0, as commented by #EricPostpischil), so I got rid of it in my example.
A modification of the answer of davidlowryduda / gsamaras to be compatible with any type of array, not just int, however that impacts the called function which gets a pointer rather than a value :
#include <stdio.h>
void for_each(void * arr, size_t nElts, size_t sizeElt, void (*fun)(void *))
{
char * p = arr;
for (size_t i = 0; i < nElts; i++)
{
(*fun)(p);
p += sizeElt;
}
}
/* for_each can be called directly or via the following macro,
the array can be an expression without any problem : is it given
in argument and the other uses are only for sizeof */
#define FOR_EACH(a, f) for_each(a, sizeof(a)/sizeof(*a), sizeof(*a), f)
void print_int(void * p)
{
printf("%d\n", *((int *) p));
}
void print_short(void * p)
{
printf("%d\n", *((short *) p));
}
int main()
{
int iarray [] = {1, 2, 3, 4};
FOR_EACH(iarray, print_int); /* for_each(iarray, sizeof(iarray)/sizeof(*iarray), sizeof(*iarray), print_int); */
short sarray [] = {5, 6, 7, 8};
FOR_EACH(sarray, print_short); /* for_each(sarray, sizeof(sarray)/sizeof(*sarray), sizeof(*sarray), print_short); */
return 0;
}
Compilation and execution:
pi#raspberrypi:~ $ gcc -pedantic -Wall -Wextra i.c
pi#raspberrypi:~ $ ./a.out
1
2
3
4
5
6
7
8
You can define a macro for this.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FOR_EACH(type, target, size, body) \
\
for (size_t i = 0; i < size; i++) \
{ \
type value = target[i]; \
body; \
}
int main(int argc, char const *argv[])
{
int arr[4] = {0, 1, 2, 3};
char *name = "My Name";
FOR_EACH(int, arr, 4, {
printf("%d\n", value);
})
size_t len = strlen(name);
FOR_EACH(char, name, len, {
printf("%c-", value);
})
return 0;
}
In this case you don't even need a function and it can be used to iterate over any array of any type as long as you know its length.
You could do something like that using function pointers. But using macros would be very more powerful, it will allow your foreach to be generic.
#include <stdio.h>
typedef struct int_array{
int *data;
int len;
}Int_array;
void print_number(int num)
{
printf("%d\n", num);
}
void for_each_function(Int_array a, void (*f)(int)){
for(int i =0; i < a.len;i++){
(*f)(a.data[i]);
}
}
int main(){
int data[4] = { 1, 2, 3, 4 };
Int_array x;
x.len = 4;
x.data = data;
for_each_function(x, print_number);
}
I took a solution's friend on forum and I increased.
I got the idea that foreach doesn't have to have a value to stop the loop.
#include <iostream>
#include <stdio.h>
void for_each(int * arr, void (*fun)(int)){
for (int i = 0; i < arr[i]; i++){
(*fun)(arr[i]);
}
}
void print_num(int num){
printf("%d\n", num);
}
int main(){
int array [] = {1, 2, 3, 4, 5, 6};
for_each(array, print_num);
}
I need to write a function that replaces 2 "numeric numbers", of otherwise unknown type.
I don't know the exact type and I can only use 2 parameters.
So this is what I have tried:
void swap(void *p1, void *p2)
{
char p;
char * q1 = (char *)p1;
char * q2 = (char *)p2;
for (int i = 0; i < sizeof(long double); i++)
{
p = q1[i];
q1[i] = q2[i];
q2[i] = p;
}
}
Usage:
double a = 100123000000.2;
double b = 100065450000.3;
printf("a: %1f, b: %1f\n", a, b);
swap(&a, &b)
printf("a: %1f, b: %1f\n", a, b);
This works fine but my question is what if my number is bigger then long double (or there is no one..)
Is my solution OK?
This works fine but my question is what if my number is bigger (?)
Is my solution is OK ?
Code will have trouble unless it knows the exact size. So, no, OP's solution is not OK.
Somehow void swap() needs to know the size of the data to swap.
i can only use 2 parameters.
Code can cheat and put all the data into 1 argument as a compound literal, since C99.
typedef struct {
void *a;
void *b;
size_t sz;
} swap_T;
// Only 1 parameter!!
void swap(swap_T sw) {
unsigned char * q1 = sw.a;
unsigned char * q2 = sw.b;
while (sw.sz > 0) {
sw.sz--;
unsigned char p = q1[sw.sz];
q1[sw.sz] = q2[sw.sz];
q2[sw.sz] = p;
}
}
int main(void) {
double a = 100123000000.2;
double b = 100065450000.3;
printf("a: %g, b: %g\n", a, b);
swap(((swap_T ) { &a, &b, sizeof a } )); // Compound literal
printf("a: %g, b: %g\n", a, b);
return 0;
}
Output
a: 1.00123e+11, b: 1.00065e+11
a: 1.00065e+11, b: 1.00123e+11
Code could wrap the swap(((swap_T ) { &a, &b, sizeof a } )) in a macro that looks like a function call of 2
#define SWAP(a,b) (swap(((swap_T ) { &(a), &(b), sizeof (a) } )))
...
SWAP(a,b);
As long as a is not an expression with a variable logic array (VLA) and with side-effects, the macro should work fine.
Of course, best if a,b are the same type.
Your program should try to retrieve sizeof of its argument to know how many bytes to swap - perhaps as a third argument - you have no way of telling otherwise how big an argument is. With current function you will overwrite memory which will end up badly - especially in a bigger program. For example, consider a following program.
#include <stdio.h>
int main() {
int a[] = {1, 2, 3, 4, 5, 6, 7, 8};
swap(&a[0], &a[4]);
for (int i = 0; i < 8; i++) {
printf("%d\n", a[i]);
}
}
Which will return 5, 6, 7, 8, 1, 2, 3, 4 - which clearly isn't expected - only 0th and 4th array elements should have been swapped.
It's possible to hide getting size behind a macro if needed.
#define SWAP(a, b) swap(&(a), &(b), sizeof(a))
I read this page to know how to use variable arguments:
https://www.tutorialspoint.com/cprogramming/c_variable_arguments.htm
All right, integer result is ok.
But when I replace its type to unsigned char, everything goes wrong:
#include <stdio.h>
#include <stdarg.h>
double average(int num,...) {
va_list valist;
double sum = 0.0;
int i;
/* initialize valist for num number of arguments */
va_start(valist, num);
/* access all the arguments assigned to valist */
for (i = 0; i < num; i++) {
sum += va_arg(valist, int);
}
/* clean memory reserved for valist */
va_end(valist);
return sum/num;
}
void foo(unsigned char arg_count,int num,...) {
va_list valist;
int i;
/* initialize valist for num number of arguments */
va_start(valist, num);
/* access all the arguments assigned to valist */
for (i = 0; i < arg_count; i++) {
printf("%02x,",va_arg(valist, int));
}
/* clean memory reserved for valist */
va_end(valist);
}
void bar(int num,...) {
va_list valist;
int i;
/* initialize valist for num number of arguments */
va_start(valist, num);
/* access all the arguments assigned to valist */
for (i = 0; i < num; i++) {
printf("%02x,",va_arg(valist, int));
}
/* clean memory reserved for valist */
va_end(valist);
}
int main() {
printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5));
printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));
foo(3,'a','b','c');
printf("\n");
bar('a','b','c');
}
Result as following:
Compiling the source code....
$gcc main.c -o demo -lm -pthread -lgmp -lreadline 2>&1
Executing the program....
$demo
Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000
62,63,400aab,
62,63,00,0a,c0a4700,4009e0,b0ef50a,01,64f5ead8,40000,400934,00,440c7120,400540,64f5ead0,00,00,83cc7120,be487120,00,00,00,bea8d73,be96d10,b6b9950,00,00,00,400540,64f5ead0,40056a,64f5eac8,c0c0180,01,64f5f3d3,00,64f5f3d8,64f5f473,64f5f48d,64f5f4a9,64f5f4b2,64f5f4c8,64f5f4e5,64f5f50d,64f5f796,64f5f7af,64f5f7d9,64f5f7f8,64f5f802,64f5f80a,64f5f823,64f5f83b,64f5f850,64f5f876,64f5f87e,64f5f898,64f5f8d0,64f5f8db,64f5f8e3,64f5f946,64f5f972,64f5f998,64f5fa2d,64f5fa63,64f5fa79,64f5ff2f,64f5ffc9,00,21,64fdb000,10,bfebfbff,06,1000,11,64,03,400040,04,38,05,09,07,be98000,08,00,09,400540,0b,30,0c,30,0d,30,0e,30,17,
Everything was the same with the int version, but why result is different?
Replace:
va_start(valist, num);
with
va_start(valist, arg_count);
in foo and change its prototype to:
void foo(unsigned char arg_count, ...)
You're declaring 3 arguments but starting the va_list 2 from the end.
Also, change this
bar('a','b','c');
to:
bar(3, 'a','b','c');
in main. You have omitted the num argument.
Maybe I don't understand what it is that you are trying to do with your code, but aren't you just sending 'a', which typically will be the character code 97, as the number of arguments to the function bar? So it tries to print 97 arguments.
va_start(valist, num) initializes valist to start with the argument after num. In both function calls, to foo and bar, 'a' is in the position of num, so the first value from va_arg will be 'b', which is 98 decimal, or 62 hexadecimal.
I am writing a C version of my R mcmc code. Part of the code that is throwing up errors is as follows:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stddef.h>
#include <limits.h>
#include <gsl/gsl_machine.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_cdf.h>
#include <R.h>
#include <Rmath.h>
#include <Rembedded.h>
#include <Rdefines.h>
#include <R_ext/BLAS.h>
#include <R_ext/Lapack.h>
#include <R_ext/Linpack.h>
//#include <vecLib/cblas.h> /* C BLAS in APPLE */
#include "blaio.h" /*Linear Algebra I/O using http://www.mitchr.me/SS/exampleCode/blas.html*/
double rTruncNorm (double, double*, double, double);
// C version of group lasso
void BinaryBayesianGroupLasso (double *X, int *dim, int *y, int *iterations, double *bprev, double *Lprev) {
// X is design matrix with intercept
//dim is dimensions of X
int iter = 1, i;
int n = dim[0];
int p = dim[1];
double sigma = 1, z[n], mu[n], u, a, b;
// random number generation set up
const double lowest_double = -GSL_DBL_MAX;
const double highest_double = GSL_DBL_MAX;
const gsl_rng *gBaseRand; /* global rand number generator */
unsigned long randSeed;
/* specifying to use Mersenne twister as the PRNG */
gBaseRand = gsl_rng_alloc(gsl_rng_mt19937);
srand(time(NULL)); /* initialization for rand() */
randSeed = rand(); /* returns a non-negative integer */
gsl_rng_set(gBaseRand, randSeed); /* seed the PRNG */
while(iter < iterations){
cblas_dgemv(CblasRowMajor, CblasNoTrans, n, p, 1, X, p, bprev, 1, 0, mu, 1);
printVector(n, mu, 8, 3, NULL, NULL, NULL, " mu"); // width=8, precision=3
for (i = 0; i < n; i++) {
u = gsl_ran_flat(gBaseRand, 0, 1);
if(y[i]==1){
a = 0.0 - mu[i];
b = highest_double;
} else {
a = lowest_double;
b = 0.0 - mu[i];
}
z[i] = mu[i] + rTruncNorm(sigma, &u, a, b);
}
printVector(n, z, 8, 3, NULL, NULL, NULL, " mu"); // width=8, precision=3
}
gsl_rng_free(gBaseRand);
}
// gaussian truncated normal between [a,b], params - mu=0, sigma, unifrnd = u
double rTruncNorm(double sigma, double *u, double a, double b){
double unifr = *u;
double Fa = gsl_cdf_gaussian_P(a, sigma);
double Fb = gsl_cdf_gaussian_P(b, sigma);
double v = Fa + (Fb-Fa)*unifr;
double x = gsl_cdf_gaussian_Pinv(v, sigma);
return(x);
}
The functions in blaio.h have already been built using the makefile in the website, I got them from.
I am using the following to compile:
R CMD SHLIB -lgsl -lgslcblas BinaryBayesianGroupLasso.c
It is throwing the following errors:
gcc -arch x86_64 -std=gnu99 -I/Library/Frameworks/R.framework/Resources/include -I/Library/Frameworks/R.framework/Resources/include/x86_64 -DNDEBUG -I/usr/local/include -fPIC -g -O2 -c BinaryBayesianGroupLasso.c -o BinaryBayesianGroupLasso.o
BinaryBayesianGroupLasso.c: In function ‘BinaryBayesianGroupLassoC’:
BinaryBayesianGroupLasso.c:36: error: nested functions are disabled, use -fnested-functions to re-enable
BinaryBayesianGroupLasso.c:36: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘gBaseRand’
BinaryBayesianGroupLasso.c:37: warning: implicit declaration of function ‘time’
BinaryBayesianGroupLasso.c:41: warning: comparison between pointer and integer
BinaryBayesianGroupLasso.c:50: warning: implicit declaration of function ‘cblas_dgemv’
BinaryBayesianGroupLasso.c:50: error: ‘CblasRowMajor’ undeclared (first use in this function)
BinaryBayesianGroupLasso.c:50: error: (Each undeclared identifier is reported only once
BinaryBayesianGroupLasso.c:50: error: for each function it appears in.)
BinaryBayesianGroupLasso.c:50: error: ‘CblasNoTrans’ undeclared (first use in this function)
BinaryBayesianGroupLasso.c:50: error: ‘mu’ undeclared (first use in this function)
BinaryBayesianGroupLasso.c:51: warning: implicit declaration of function ‘printVector’
BinaryBayesianGroupLasso.c:70: error: ‘i’ undeclared (first use in this function)
BinaryBayesianGroupLasso.c:71: error: ‘u’ undeclared (first use in this function)
BinaryBayesianGroupLasso.c:72: error: ‘y’ undeclared (first use in this function)
BinaryBayesianGroupLasso.c:73: error: ‘a’ undeclared (first use in this function)
BinaryBayesianGroupLasso.c:74: error: ‘b’ undeclared (first use in this function)
BinaryBayesianGroupLasso.c:79: warning: implicit declaration of function ‘rTruncNorm’
BinaryBayesianGroupLasso.c:91: warning: passing argument 1 of ‘gsl_rng_free’ discards qualifiers from pointer target type
BinaryBayesianGroupLasso.c:92: warning: ‘return’ with a value, in function returning void
BinaryBayesianGroupLasso.c: At top level:
BinaryBayesianGroupLasso.c:98: error: conflicting types for ‘rTruncNorm’
BinaryBayesianGroupLasso.c:79: error: previous implicit declaration of ‘rTruncNorm’ was here
make: *** [BinaryBayesianGroupLasso.o] Error 1
I have no nested function in C. Why is it throwing up such an error? If it is a linking problem, should it not throw up function not found kind of an error?
Edit: I was asked to post the solution. In my initial code, I was not compiling the above code (but an older version in another directory). (Yup, that is the stupidest error possible). I am reposting a working version of the code. I do not think it would be of much value, other than seeing an example of how mcmc sampling steps are coded up, and how its called from R. This code is however incomplete in terms of Grouped Bayesian Lasso. It also has codes (with sources attributed for printing matrices used in blas). I have started coding in C in just about a week, so take everything (except the print codes) with a pinch of salt. If you have much a prettier version in mind (like putting separate files for printing, and the compiling command), go ahead and make changes.
I have learned 3 things in this exercise:
calling C from R
using blas
using gsl
The code will evolve to address a 4th point later:
- calling R from C
I have to do a sampling from generalized inverse gaussian in this model (excluded in this code). However I do not see an easy piece of function that does that in C. I might have to use R's function then. So it becomes weird. R calls this C function, and within this C function a few other R functions will be called. Your comments will be appreciated, if there are any alternate approaches, and if this is the way, how to do it - RInside? anything easier?
Time is of prime importance to me. I have to run this model with about a million data points, with at least 10,000 mcmc iterations. I want to finish this in less than 5 minutes, preferably 2 minutes.
A third stage of this exercise is to parallelize the sampling step in z.
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stddef.h>
#include <limits.h>
#include <time.h>
#include <ctype.h> /* Char classes ISOC */
#include <string.h> /* Strings ISOC */
#include <gsl/gsl_machine.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_cdf.h>
#include <gsl/gsl_cblas.h>
#include <R.h>
#include <Rmath.h>
#include <Rembedded.h>
#include <Rdefines.h>
//#include <R_ext/BLAS.h>
#include <R_ext/Lapack.h>
#include <R_ext/Linpack.h>
//#include <vecLib/cblas.h> /* C BLAS APPLE */
//#include "blaio.h" /* Basic Linear Algebra I/O */
// source for the printing codes: //source: http://www.mitchr.me/SS/exampleCode/blas.html
#define REALT double
#ifndef REALT
#define REALT double
#endif
void sgeprt(int m, int n, REALT, char *c);
void dgeprt(int m, int n, REALT, char *c);
/* ****************************** - external functions from blaio.h. */
void printVector(int n, REALT *v, /* Size and array */
int wide, int prec, /* Width and precesion for floats */
char *pad, /* Right pad string */
char *ldel, char *rdel, /* Left and right delimiter */
char *tag /* Tag for first line */
);
void printMatrix(const enum CBLAS_ORDER order,
int n, int m, REALT *a, /* Size and array */
int wide, int prec, /* Width and precesion for floats */
char *pad, /* Right pad string */
char *ldel, char *rdel, /* Left and right delimiter */
char *lidel, char *ridel, /* Left and right INNER delimiter */
char *tag /* Tag for first line */
);
int readMatrix(int *n, int *m, /* Will contain size of the array after the read */
REALT *a, /* Will point to the data */
int maxEle, /* Maximum number of elements to read */
char *fileName /* The file name to read the data from */
);
void printMatrixThr(const enum CBLAS_ORDER order,
int n, int m, REALT *a, /* Size and array */
char *inStr, char *outStr, /* "in" string, and "out" string */
REALT minIn, REALT maxIn, /* Min/Max values for "in" range. */
char *pad, /* Right pad string */
char *ldel, char *rdel, /* Left and right delimiter */
char *lidel, char *ridel, /* Left and right INNER delimiter */
char *tag /* Tag for first line */
);
double rTruncNorm (double, double*, double, double);
// C version of group lasso
void BinaryBayesianGroupLassoC (double *X, int *dim, int *y, int *iterations, double *bprev, double *Lprev) {
// X is design matrix with intercept
//dim is dimensions of X
int iter = 1, i;
int n = dim[0];
int p = dim[1];
double sigma = 1, z[n], mu[n], u, a, b, *Li;
// random number generation set up
const double lowest_double = -GSL_DBL_MAX;
const double highest_double = GSL_DBL_MAX;
const gsl_rng *gBaseRand; /* global rand number generator */
unsigned long randSeed;
/* specifying to use Mersenne twister as the uniform PRNG */
gBaseRand = gsl_rng_alloc(gsl_rng_mt19937);
srand(time(NULL)); /* initialization for rand() */
randSeed = rand(); /* returns a non-negative integer */
gsl_rng_set(gBaseRand, randSeed); /* seed the PRNG */
while(iter < *iterations){
cblas_dgemv(CblasRowMajor, CblasNoTrans, n, p, 1, X, p, bprev, 1, 0, mu, 1);
printVector(n, mu, 8, 3, NULL, NULL, NULL, " mu"); // width=8, precision=3
for (i = 0; i < n; i++) {
u = gsl_ran_flat(gBaseRand, 0, 1);
if(y[i]==1){
a = 0.0 - mu[i];
b = highest_double;
} else {
a = lowest_double;
b = 0.0 - mu[i];
}
z[i] = mu[i] + rTruncNorm(sigma, &u, a, b);
}
printVector(n, z, 8, 3, NULL, NULL, NULL, " z"); // width=8, precision=3
iter = iter + 1;
}
gsl_rng_free(gBaseRand);
}
// gaussian truncated normal between [a,b], params - mu=0, sigma, unifrnd = u
double rTruncNorm(double sigma, double *u, double a, double b){
double unifr = *u;
double Fa = gsl_cdf_gaussian_P(a, sigma);
double Fb = gsl_cdf_gaussian_P(b, sigma);
double v = Fa + (Fb-Fa)*unifr;
double x = gsl_cdf_gaussian_Pinv(v, sigma);
return(x);
}
/* function definitions from blaio.h *******/
void printMatrixUbr(const enum CBLAS_ORDER order, /* CBLAS row order */
int n, int m, REALT *a, /* Size and array */
char *inStr, char *outStr, /* "in" string, and "out" string */
REALT minIn, REALT maxIn, /* Min/Max values for "in" range. */
int wide, int prec, /* Width and precesion for floats */
int *rowPerm, int *colPerm,
char prtMode, /* b=bitmap, V=values, *=in/out) */
char *fileName,
int maskMode, // (L-below diag, U=above diag, D-Diagnal, M-Mask, 0=NONE, \0-NONE
char *mask,
char *pad, /* Right pad string */
char *ldel, char *rdel, /* Left and right delimiter */
char *lidel, char *ridel, /* Left and right INNER delimiter */
char *tag /* Tag for first line */
);
/* ************************************************************************** */
void printVector(int n, REALT *v, int wide, int prec, char *pad, char *ldel, char *rdel, char *tag) {
printMatrixUbr(CblasRowMajor, 1, n, v, NULL, NULL, 0.0, 0.0, wide, prec, NULL, NULL, 'V', NULL, '0', NULL, pad, ldel, rdel, "", "", tag);
} /* end func printVector */
/* ************************************************************************** */
void printMatrix(const enum CBLAS_ORDER order, int n, int m, REALT *a, int wide, int prec, char *pad, char *ldel, char *rdel, char *lidel, char *ridel, char *tag) {
printMatrixUbr(order, n, m, a, NULL, NULL, 0.0, 0.0, wide, prec, NULL, NULL, 'V', NULL, '0', NULL, pad, ldel, rdel, lidel, ridel, tag);
} /* end func printMatrix */
/* ************************************************************************** */
void printMatrixThr(const enum CBLAS_ORDER order, int n, int m, REALT *a, char *inStr, char *outStr, REALT minIn, REALT maxIn, char *pad, char *ldel, char *rdel, char *lidel, char *ridel, char *tag) {
printMatrixUbr(order, n, m, a, inStr, outStr, minIn, maxIn, 0, 0, NULL, NULL, '*', NULL, '0', NULL, pad, ldel, rdel, lidel, ridel, tag);
} /* end func printMatrixThr*/
/* ************************************************************************** */
void printMatrixUbr(const enum CBLAS_ORDER order, /* CBLAS row order */
int n, int m, REALT *a, /* Size and array */
char *inStr, char *outStr, /* "in" string, and "out" string */
REALT minIn, REALT maxIn, /* Min/Max values for "in" range. */
int wide, int prec, /* Width and precesion for floats */
int *rowPerm, int *colPerm, /* Permute rows i->xx[i] */
char prtMode, /* b=bitmap, V=values, *=in/out */
char *fileName, /* if NULL, stdout. */
int maskMode, /* L, U, D, M-Mask, 0=NONE */
char *mask, /* Mask (same size as a) ctrl print */
char *pad, /* Right pad string */
char *ldel, char *rdel, /* Left and right delimiter */
char *lidel, char *ridel, /* Left and right INNER delimiter */
char *tag /* Tag for first line */
) {
int i, j, iP, jP;
int k, ldelLen, tagLen, cIdx, prtPerMask;
REALT pVal;
if(inStr == NULL) inStr = "*";
if(outStr == NULL) outStr = " ";
if(wide < 0) wide = 5;
if(prec < 0) prec = 2;
if(ldel == NULL) ldel = "[";
if(ridel == NULL) ridel = "]";
if(lidel == NULL) lidel = "[";
if(rdel == NULL) rdel = "]";
if(pad == NULL) pad = " ";
if(tag == NULL) tag = "";
ldelLen = strlen(ldel);
tagLen = strlen(tag);
for(j=0; j<n; j++) {
if(j==0)
printf("%s%s%s%s", tag, ldel, lidel, pad);
else {
for(k=0;k<tagLen;k++) printf(" ");
for(k=0;k<ldelLen;k++) printf(" ");
printf("%s%s", lidel, pad);
} /* end if/else */
for(i=0; i<m; i++) {
if(colPerm != NULL)
iP = colPerm[i];
else
iP = i;
if(rowPerm != NULL)
jP = rowPerm[j];
else
jP = j;
if(order == CblasColMajor)
cIdx = n*iP+jP;
else
cIdx = m*jP+iP;
pVal = a[cIdx];
// Figure out what the mask has to do with printing..
if(maskMode == '0')
prtPerMask = 1;
else if(maskMode == 'L')
prtPerMask = (iP<jP); // Row order specific! Fix this.
else if(maskMode == 'D')
prtPerMask = (iP==jP);
else if(maskMode == 'U')
prtPerMask = (iP>jP); // Row order specific! Fix this.
else if(maskMode == 'M')
prtPerMask = mask[cIdx];
else
prtPerMask = 1;
if(prtMode == '*') {
if( prtPerMask && (pVal >= minIn) && (pVal <= maxIn) )
printf("%s%s", inStr, pad);
else
printf("%s%s", outStr, pad);
} else {
if(prtPerMask)
printf("%*.*f%s", wide, prec, pVal, pad);
else
printf("%*s%s", wide, outStr, pad);
} /* end if/else */
} /* end for */
if(j==n-1)
printf("%s%s\n", ridel, rdel);
else
printf("%s\n", ridel);
} /* end for */
} /* end func printMatrixUbr*/
/* ************************************************************************** */
int readMatrix(int *n, int *m, REALT *a, int maxEle, char *fileName) {
int i, j;
int inComment;
int numNumbers;
int ch;
int lengthOfNumber;
char numberBuffer[256];
FILE *FP;
FP = fopen(fileName, "r");
if(FP == NULL)
return 1;
i = j = 0;
inComment = 0;
lengthOfNumber = -1;
numNumbers = 0;
while(EOF != (ch = getc(FP))) {
if(ch == '#') inComment = 1; /* Enter comment upon # char. */
if(inComment && (ch < 20)) inComment = 0; /* Break out of comment upon ANY control char. */
if( !(inComment)) {
if(isdigit(ch) || (ch=='.') || (ch=='E') || (ch=='e') || (ch=='-') | (ch=='+')) {
lengthOfNumber++;
numberBuffer[lengthOfNumber] = ch;
} else {
if(lengthOfNumber>=0) {
numberBuffer[lengthOfNumber+1] = '\0';
lengthOfNumber = -1;
numNumbers++;
if(numNumbers==1) {
*n = atoi(numberBuffer);
} else if(numNumbers==2) {
*m = atoi(numberBuffer);
} else if (numNumbers<maxEle) {
a[numNumbers-3] = atof(numberBuffer);
} else {
return(-1);
} /* end if/else */
} /* end if */
} /* end if/else */
} /* end if */
} /* end while */
fclose(FP);
return(numNumbers-2);
} /* end func main */
Simulating random data in R and calling C function:
# simulate data
n = 1000000; p=30; iterations = 1000; burnin = 5;
X = as.data.frame(sapply(1:p, function(x) rnorm(n, 0, 1)))
Y = sample(c(0,1), n, replace=TRUE)
call_BinaryBayesianGroupLasso <- function(X,Y,iterations,burnin){
# X is design matrix without intercept
p = dim(X)[2]+1 # number of predictors including dummy vars and intercept
n = dim(X)[1]
z = rep(0,n)
h = 1000 # hyperparameter for shrinkage (controls number of parameters in model)
b = matrix(0, nrow=p, ncol=iterations) # store beta
L = matrix(0, nrow=p, ncol=iterations) # store lambda in
initmodel = glm(Y ~ ., data = X, family = binomial(link = "probit")) # full model initial estimates
# add intercept to design matrix
X = cbind(rep(1,n), X)
colnames(X) = names(initmodel$coefficients)
b[,1] = summary(initmodel)$coefficients[,1]
L[,1] = summary(initmodel)$coefficients[,2]
eye = diag(rep(1,n)) # used in mcmc step for b
bprev=b[,1]
Lprev=L[,1]
# call C function for group lasso
res = .C("BinaryBayesianGroupLassoC",
as.double(as.matrix(X)),
as.integer(dim(X)),
as.integer(Y),
as.integer(iterations),
as.double(bprev),
as.double(Lprev))
}
I have functions that I wish to call based on some input. Each function has different number of arguments. In other words,
if (strcmp(str, "funcA") == 0) funcA(a, b, c);
else if (strcmp(str, "funcB") == 0) funcB(d);
else if (strcmp(str, "funcC") == 0) funcC(f, g);
This is a bit bulky and hard to maintain. Ideally, these are variadic functions (e.g., printf-style) and can use varargs. But they are not. So exploiting the cdecl calling convention, I am stuffing the stack via a struct full of parameters. I'm wondering if there's a better way to do it. Note that this is strictly for in-house (e.g., simple tools, unit tests, etc.) and will not be used for any production code that might be subjected to malicious attacks.
Example:
#include <stdio.h>
typedef struct __params
{
unsigned char* a;
unsigned char* b;
unsigned char* c;
} params;
int funcA(int a, int b)
{
printf("a = %d, b = %d\n", a, b);
return a;
}
int funcB(int a, int b, const char* c)
{
printf("a = %d, b = %d, c = %s\n", a, b, c);
return b;
}
int funcC(int* a)
{
printf("a = %d\n", *a);
*a *= 2;
return 0;
}
typedef int (*f)(params);
int main(int argc, char**argv)
{
int val;
int tmp;
params myParams;
f myFuncA = (f)funcA;
f myFuncB = (f)funcB;
f myFuncC = (f)funcC;
myParams.a = (unsigned char*)100;
myParams.b = (unsigned char*)200;
val = myFuncA(myParams);
printf("val = %d\n", val);
myParams.c = (unsigned char*)"This is a test";
val = myFuncB(myParams);
printf("val = %d\n", val);
tmp = 300;
myParams.a = (unsigned char*)&tmp;
val = myFuncC(myParams);
printf("a = %d, val = %d\n", tmp, val);
return 0;
}
Output:
gcc -o func func.c
./func
a = 100, b = 200
val = 100
a = 100, b = 200, c = This is a test
val = 200
a = 300
a = 600, val = 0
Rather than trying to torture this out of corner cases in the language, I would do this in a much more straightforward way, by defining variadic wrapper functions. You can do this generically with macros:
#define WRAP_VARIADIC_2_ARG(FNAME, RETTYPE, ARGTYPE1, ARGTYPE2) \
(RETTYPE) wrapped_##FNAME(...) { \
va_list args; \
va_start(args, 2); \
(ARGTYPE1) arg1 = va_arg(args, (ARGTYPE1)); \
(ARGTYPE2) arg2 = va_arg(args, (ARGTYPE2)); \
va_end(args); \
return FNAME(arg1, arg2); \
}
And similar macros for other arg counts. Then, you call:
WRAP_VARIADIC_2_ARG(funcA, int, int, int)
WRAP_VARIADIC_3_ARG(funcB, int, int, int, const char*)
WRAP_VARIADIC_1_ARG(funcC, int, int*)
This will define a set of functions with the following signatures that you can use in your dispatching function:
int wrapped_funcA(...)
int wrapped_funcB(...)
int wrapped_funcC(...)
This should be straightforward from there.