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.
Related
I have a union type declared as follows:
typedef union Data {
int i;
char c;
double d;
float f;
} data;
I know fields are overwritten by new values. I would like to know if there is any way to do the following instead of needing to manually access each field depending on the type of data I want to store:
data *c;
*c = 3; // instead of c.i = 3; c.i should be 3
*c = 'a' // instead of c.c = 'a'; c.c should be 'a', c.i should no longer be 3;
I tried doing as written above, but I get an error saying:
Assigning to 'data' (aka 'union Data') from incompatible type 'int'.
Is there any way to do this?
Here is an alternative approach with a tagged union and a polymorphic instantiation macro. Note however that 'a' has type int in C and char in C++, so it must be cast as (char)'a' to have type char in both languages.
#include <stdio.h>
typedef struct {
enum { CHAR, INT, FLOAT, DOUBLE } type;
union {
char c;
int i;
float f;
double d;
};
} data;
#define data(v) _Generic((v), \
char: (data){ .type = CHAR, .c = (v) }, \
int: (data){ .type = INT, .i = (v) }, \
float: (data){ .type = FLOAT, .f = (v) }, \
double: (data){ .type = DOUBLE, .d = (v) })
void print(data x) {
switch (x.type) {
case CHAR: printf("char: '%c'\n", x.c); break;
case INT: printf("int: %d\n", x.i); break;
case FLOAT: printf("float: %gf\n", (double)x.f); break;
case DOUBLE: printf("double: %g\n", x.d); break;
}
}
int main() {
data a = data((char)'a'); // char
data b = data('a'); // int
data c = data(3); // int
data d = data(1.2f); // float
data e = data(3.14); // double
print(a);
print(b);
print(c);
print(d);
print(e);
return 0;
}
Output:
char: 'a'
int: 97
int: 3
float: 1.2f
double: 3.14
No. It's not possible. If you want type switching use _Generic but 'a' is an integer character constant (i.e. type is int) so you will only find partial success with sample input provided:
#include <stdio.h>
typedef union {
char c;
double d;
float f;
int i;
} data;
#define set(d, v) _Generic((v),\
char: setc,\
double: setd,\
float: setf,\
int: seti\
)((d), (v))
void setc(data d, int v) {
d.c = v;
printf("c = %c\n", d.c);
}
void setd(data d, double v) {
d.d = v;
printf("d = %lf\n", d.d);
}
void seti(data d, int v) {
d.i = v;
printf("i = %d\n", d.i);
}
void setf(data d, float f) {
d.f = f;
printf("f = %f\n", d.f);
}
int main() {
data d = { 0 };
set(d, 'a'); // seti()
set(d, (char) 'c');
set(d, 3.14);
set(d, 1.2f);
set(d, 3);
}
and the resulting output:
i = 97
c = c
d = 3.140000
f = 1.200000
i = 3
I have this code which is supposed to swap two values inside a_ptr and b_ptr:
// swaps two values inside two variables of any type
void swap(void *a_ptr, void *b_ptr)
{
size_t size = sizeof(void *);
void *tmp = malloc(size);
memcpy(tmp, a_ptr, size);
memcpy(a_ptr, b_ptr, size);
memcpy(b_ptr, tmp, size);
free(tmp);
// char wot0 = 0;
// char wot1 = 0;
// swap(&wot0, &wot1);
}
int main(){
long a = -7;
long b = 99999999;
printf("A: %ld, B: %ld\n", a, b);
swap(&a, &b);
printf("A: %ld, B: %ld\n", a, b);
printf("\n");
short c = -9;
short d = 11111;
printf("C: %hd, D: %hd\n", c, d);
swap(&c, &d);
printf("C: %hd, D: %hd\n", c, d);
printf("\n");
char ca = 'a';
char cx = 'x';
printf("CA: %c CX: %c\n", ca, cx);
swap(&ca, &cx);
printf("CA: %d CX: %c\n", ca, cx);
printf("\n");
char *str0 = "Hello, ";
char *str1 = "World!";
printf("STR0: %s STR1: %s\n", str0, str1);
swap(&str0, &str1);
printf("STR0: %s STR1: %s\n", str0, str1);
printf("\n");
return 0;
}
However the output is:
A: -7, B: 99999999
A: 99999999, B: -7
C: -9, D: 11111
C: -7, D: -9
CA: a CX: x
CA: -9 CX: a
STR0: Hello, STR1: World!
STR0: World! STR1: Hello,
It successfully swaps a and b, and then somehow replaces c with b, and ca with d, how's that even possible?
Also, uncommenting these lines:
// char wot0 = 0;
// char wot1 = 0;
// swap(&wot0, &wot1);
Leads to a segfault, why?
EDIT:
I think I didin't convey my intentions very well. That I basically want to do is swap pointers so that a_ptr points to value inside b, and b_ptr points to value inside a, I don't want to actually copy the values themselves and I think I achieved that somewhat successfully, strings of different lengths (for examples "Foo" and "Hello, World!") get swapped without any issues, I tested that, however I don't understand why some variables don't get swapped and actually point to values outside of that I passed into the function
sizeof(void *); is a constant (usually 4 or 8) and does not give you the size of the object it's pointing at. When you copy size bytes, you are not copying the correct amount for the types used.
You're probably better off by supplying the size of the type to the function:
// swaps two values inside two variables of any type
void swapper(void *a_ptr, void *b_ptr, size_t size)
{
void *tmp = malloc(size);
memcpy(tmp, a_ptr, size);
memcpy(a_ptr, b_ptr, size);
memcpy(b_ptr, tmp, size);
free(tmp);
}
// Generate compilation error if objects of different sizes are used.
// The false switch case (0) can only be defined once so if the sizes
// are not the same, it'll try to redefine "case 0" and fail compiling:
#define compile_assert(_expr) switch (_expr) { case 0: break; case _expr: break; }
#define swap(x,y) do { \
compile_assert(sizeof(*(x)) == sizeof(*(y))); \
swapper((x),(y),sizeof(*(x))); } while (0)
And call it like you aimed to do:
swap(&a, &b);
If you only need to swap fundamental types, you could make different implementations for all of them. This should also make it safer since it's harder to supply pointers to objects of different types this way:
#define SwapBuilder(name,type) \
void name(type *a, type *b) { type tmp = *a; *a = *b; *b = tmp; }
SwapBuilder(swap_char,char)
SwapBuilder(swap_schar,signed char)
SwapBuilder(swap_uchar,unsigned char)
SwapBuilder(swap_short,short)
SwapBuilder(swap_ushort,unsigned short)
SwapBuilder(swap_int,int)
SwapBuilder(swap_uint,unsigned int)
SwapBuilder(swap_long,long)
SwapBuilder(swap_ulong,unsigned long)
SwapBuilder(swap_longlong,long long)
SwapBuilder(swap_ulonglong,unsigned long long)
SwapBuilder(swap_float,float)
SwapBuilder(swap_double,double)
SwapBuilder(swap_longdouble,long double)
// A _Generic to call the correct function:
#define swap(x,y) _Generic((x), \
char* : swap_char, \
signed char* : swap_schar, \
unsigned char* : swap_uchar, \
short* : swap_short, \
unsigned short* : swap_ushort, \
int* : swap_int, \
unsigned int* : swap_uint, \
long* : swap_long, \
unsigned long* : swap_ulong, \
long long* : swap_longlong, \
unsigned long long* : swap_ulonglong, \
float* : swap_float, \
double* : swap_double, \
long double* : swap_longdouble \
)((x),(y))
And you'd still call it with swap(&a, &b);
i need get some logs from uses of variables in C code;
For example from the following code:
int main(){
int a,b,c;
a=1;
b=1;
c= a==0||b==1
return 0;
}
i make:
int log(int v){
//print log
return v;
}
int main(){
int a,b,c;
a=1;
b=1;
c= log(a)==0||log(b)==1
return 0;
}
this work perfectly, but with all variables int.
how can i do this for variable of any type?
#include <stdio.h>
#define LOG(TYPE, STRING, VAR) \
(printf(STRING, VAR), (TYPE) VAR)
int main()
{
int j = 3;
double q = 2.3;
double s;
s = LOG(int, "j=%d\n", j) + LOG(double, "q=%lf\n", q);
LOG(double, "s=%lf\n", s);
}
j=3
q=2.300000
s=5.300000
One caution: This evaluates the VAR expression twice, so it should always be the contents of a regular variable, not a more complex expression. You can replace the call to printf with a call to a logging operation that uses varargs.
Log4c is your friend:
Log4c is a library of C for flexible logging to files, syslog and other destinations. It is modeled after the Log for Java library (http://jakarta.apache.org/log4j/), staying as close to their API as is reasonable. Here is a short introduction to Log4j which describes the API, and design rationale.
how can i do this for variable of any type?
To switch code based on various types, use _Generic() to select type specific functions.
int log_int(int v) {
printf("(int %d)\n", v);
return !!v;
}
int log_double(double v) {
printf("(double %e)\n", v);
return !!v;
}
int log_char_ptr(char *v) {
printf("(str %s)\n", v);
return !!v;
}
#define mylog(X) _Generic((X), \
int: log_int, \
double: log_double, \
char *: log_char_ptr \
)(X)
Now code only needs to call mylog(various_types).
int main(void) {
int i = 3;
double d = 4.0;
char *s = "5";
mylog(i)==0||mylog(d)==0||mylog(s)==0;
return 0;
}
Output
(int 3)
(double 4.000000e+00)
(str 5)
I'd like to write some functions in C but they have to be available for all numeric types (int, float, double). What is good practise? Use pointer on void (and pointer to function of course)? Or write a different function for every type?
For example:
float func(float a, float b) {
return a+b;
}
If you can use C11, _Generic can help:
#include <stdio.h>
int ifunc(int a, int b) { return a+b; }
float ffunc(float a, float b) { return a+b; }
double dfunc(double a, double b) { return a+b; }
#define func(x, y) \
_Generic((x), int: ifunc, float: ffunc, double: dfunc, default: ifunc)(x, y)
int main(void)
{
{
int a = 1, b = 2, c;
c = func(a, b);
printf("%d\n", c);
}
{
float a = .1f, b = .2f, c;
c = func(a, b);
printf("%f\n", c);
}
{
double a = .1, b = .2, c;
c = func(a, b);
printf("%f\n", c);
}
return 0;
}
As C does not have multiple dispatch (function overloading) like C++ (EDIT: unless you use C11, which has _Generic) , you have to name the functions for each type differently, like funcInt(int a, int b); funcFloat(float a, float b);
OR
use GCC style statement-expression macros which allow typeof() to kind of fake it.
The old style C way is to use a union and a discriminator:
union multi_num {
uint32_t small_num;
uint64_t large_num;
float float_num;
};
struct multi_type {
int discriminator;
union multi_num val;
};
int
add_fun (struct multi_type var1, struct multi_type var2, struct multi_type *ret_val)
{
if (var1.discriminator != var2.discriminator) {
return -1;
}
ret_val->discriminator = var1.discriminator;
switch (var1.discriminator) {
case 1:
ret_val->val.small_num = var1.val.small_num + var2.val.small_num;
break;
case 2:
ret_val->val.large_num = var1.val.large_num + var2.val.large_num;
break;
case 3:
ret_val->val.float_num = var1.val.float_num + var2.val.float_num;
break;
}
return 0;
}
Then you can call it like this:
struct multi_type var1, var2, var3 = {0};
var1.discriminator = 1;
var2.discriminator = 1;
var1.val.small_num = 1;
var2.val.small_num = 12;
ret = add_fun(var1, var2, &var3);
if (0 == ret) {
printf("add worked, ret is %d\n", var3.val.small_num);
} else {
printf("add failed\n");
}
In C++, this would have easily been done with templates or function overloading.
The only way I know in C is to write a different function for every type.
You can use a function taking two void* and compute with them, but it's extremely misleading for the user: what if there is a special type for which another version of the function is available but the user uses the void* function? This can lead to big problems, and is not recommended.
For a practical example of this, look at the standard string conversion functions atoi, atol, atof located in stdlib.h. One function per variable type.
Can we pass arguments of different datatypes to same variadic function at the same time?
sure, look at common usages of printf:
printf("Error %d: %s", errNum, errTxt);
so ross$ expand < variadic.c && cc -Wall -Wextra variadic.c
#include <stdio.h>
#include <stdarg.h>
void f(int, ...);
struct x { int a, b; } y = { 5, 6 };
int main(void) {
float q = 9.4;
f(0, 1.234, &q, "how now", 123, &y);
return 0;
}
void f(int nothing, ...) {
va_list ap;
va_start(ap, nothing);
double f = va_arg(ap, double);
float *f2 = va_arg(ap, float *);
char *s = va_arg(ap, char *);
int i = va_arg(ap, int);
struct x *sx = va_arg(ap, struct x *);
va_end(ap);
printf("%5.3f %3.1f %s %d %d/%d\n", f, *f2, s, i, sx->a, sx->b);
}
so ross$ ./a.out
1.234 9.4 how now 123 5/6
Here's a printf-free example ( old version: http://codepad.org/vnjFj7Uh )
#include <stdarg.h>
#include <stdio.h>
/* return the maximum of n values. if n < 1 returns 0 */
/* lying to the compiler is not supported in this version, eg: **
** va_max(4, 8, 8, 8, 8, 8, 8, 8, 8) **
** or **
** va_max(4, 2, 2) **
/* is a bad way to call the function (and invokes Undefined Behaviour) */
int va_max(int n, ...) {
int res;
va_list arg;
if (n < 1) return 0;
va_start(arg, n);
n--;
res = va_arg(arg, int);
while (n--) {
int cur = va_arg(arg, int);
if (cur > res) res = cur;
}
return res;
}
int main(void) {
int test6 = va_max(6, 1, 2, 3, 4, 5, 6);
int test3 = va_max(3, 56, 34, 12);
if (test6 == 6) puts("6");
if (test3 == 56) puts("56");
return 0;
}
I made a function to unpack binary data using a varadic function,it takes different types based on
what you want "encoded/decoded".
You'd use it like:
uint32_t a;
uint16_t b;
uint16_t c;
uint8_t *buf = ....;
depickle(buf,"sis",&a,&b,&c);
where 's' expects an uint16_t* and decodes 2 bytes from buf into a as little endian, 'i' says to decode 4 bytes as little endian into 'b'
Or to e.g. decode 4 bytes as big endian into a uint32_t:
uint32_t a;
uint8_t buf[] = {0x12,0x34,0x56,0x78};
depickle(buf,"I",&a);
There's still an early version around here.