Suppose we have a list of events (int) and actions (function pointers), as in:
typedef struct action_type {
int event;
int (*func)();
} action_type;
action_type actions[] = {
{ my_event1, my_action1 },
{ my_event2, my_action2 },
{ my_event3, my_action3 },
{ my_event4, my_action1 }
}
and i want to add conditions to actually running the action as part of the table, for
better readability and
simplification of the action functions.
action_type actions[] = {
{ my_event1, exceeds(&temperature, 100) , my_action1 },
{ my_event1, between(&temperature, 50, 80), my_action2 },
...
}
is there a way to get a notation like this?
Or would I need to create something like:
action_type actions[] = {
{ my_event1, $temperature, exceeds, 100, my_action1 },
...
}
However, this approach allows only a fixed number of parameters with a fixed type. As I am writing a library and conditions could be pretty much anything, I am looking for a way to allow different variable types and different parameter counts for the condition() function.
Edit: Added additional info on the conditions possibly having different numbers and types of parameters.
I'd use an X_MACRO, ie: something like this:
#define MY_EVENT_1 1
#define MY_EVENT_2 2
#define MY_EVENT_3 3
static int my_action1(void) { printf("Over 100!\n"); return 0; }
static int my_action2(void) { printf("Over 120!\n"); return 0; }
static int my_action3(void) { printf("Over 160!\n"); return 0; }
static int exceeds(int *val, int temp) { return *val > temp; }
typedef struct action_type {
int event;
int (*func)();
} action_type;
#define X_ACTIONS \
X(MY_EVENT_1, exceeds(&temperature, 100), my_action1) \
X(MY_EVENT_2, exceeds(&temperature, 120), my_action2) \
X(MY_EVENT_3, exceeds(&temperature, 160), my_action3)
static action_type actions[] = {
#define X(type, cond, cb) { type, cb },
X_ACTIONS
#undef X
};
int main() {
int temperature = 130;
#define X(type, cond, cb) if (cond) cb();
X_ACTIONS
#undef X
return 0;
}
you can do something like this
#define __NO__FUNC NULL
typedef unsigned char (*func)(void);
typedef unsigned char (*con)(unsigned char param1, unsigned char param2, unsigned char param3);
typedef struct {
int event
func custom_func;
con condition;
} action_type;
then use like this
action_type action[] = {
{ 1,SomeFunc, SomeCondition },
{ 2,__NO__FUNC,__NO__FUNC },
};
Where some func must retun unsigned char and have input type void such as
unsigned char SomeFunc(void)
{
// some logic
return 1;
}
unsigned char SomeCondition(unsigned char param1, unsigned char param2, unsigned char param3)
{
// logic
return 1;
}
and use it like this
if (action[0].condition(1,2,3)){
action[0].custom_func();
}
you can also check if a function is set with the following example:
if (action[0].condition(1,2,3)){
if(action[0].custom_func)
action[0].custom_func();
}
Example usage all together
unsigned char SomeFunc(void)
{
// some logic
return 1;
}
unsigned char SomeCondition(unsigned char param1, unsigned char param2, unsigned char param3)
{
// logic
return 1;
}
#define __NO__FUNC NULL
typedef unsigned char (*func)(void);
typedef unsigned char (*con)(unsigned char param1, unsigned char param2, unsigned char param3);
typedef struct {
int event
func custom_func;
con condition;
} action_type;
action_type action[] = {
{ 1,SomeFunc, SomeCondition },
{ 2,__NO__FUNC,__NO__FUNC },
};
void main(void)
{
unsigned char temperature = 0;
unsigned char val = 30;
unsigned char another_val = 50;
if (action[0].condition(temperature ,val,another_val )){
action[0].custom_func();
}
}
UPDATE : I have added a second function parameter, called condition which can be used to check a output before executing custom_func()
UPDATE 2 : Added the condition function to accept variables as input
UPDATE 3 : I have added a example code of how to use it, however i like to point out that in your question you cannot have a variable number of arguments inside the declaration of those functions. The C syntax does not allow that.
An element is created inside the struct of type " typedef unsigned char (*con)(unsigned char param1, unsigned char param2, unsigned char param3);
"
Meaning that those element accepts a function that returns unsigned char and has a input of (unsigned char param1, unsigned char param2, unsigned char param3);
You cannot add functions that have different return type or different parameters.
Hope it helps!
Best regards
Related
I'm using a function pointer to delegate states in C.
// states
void state1(int first, int second) {
...
}
void state2(int first, int second) {
...
}
// state pointer
void (*current_state)(int, int);
// state logic
if (condition1) {
current_state = &state_1;
} else if (condition2) {
current_state = &state_2;
}
// do the thing
(*current_state)(1, 2);
Now I'm at a point where the given arguments no longer suffice, so there is a third argument needed. Since I don't want to change all states, I wondered if it is possible to pass a quasi constant parameter along with the pointer. Something like this:
void state3(int first, int second, int third) {
...
}
// state logic
...
else if (condition3) {
// calculate the constant third argument
int param3 = 0;
current_state = &state_3(int, int, param3);
}
I there a way to get this to work?
Well I'm not sure I'd recommend it but you could do this sort of thing:
#include <stdlib.h>
#include <stdio.h>
typedef int pfun();
int alice( int a, int b) { return b; }
int bob( int a, int b, int c) { return c; }
int main( )
{
pfun* f;
f = alice;
printf( "%d\n", f( 1, 2, 3));
f = bob;
printf( "%d\n", f( 1, 2, 3));
return EXIT_SUCCESS;
}
This compiles with
gcc -o funp -Wall funp.c
without warnings and runs correctly.
The point is that a function declaration like
int f();
Says that f is a function that returns an int, with unspecified arguments. The downside is that the compiler cannot check that the arguments are of the correct type.
You could pass a *void pointer and cast it to the concrete type in the state function. All state functions will have the same signature. Something like this:
// states
struct state1_args {
int first;
int second;
};
struct state2_args {
float first;
float second;
float third;
};
void state1(void* state_args) {
struct state1_args* args = (struct state1_args*)state_args;
use(args->first);
...
}
void state2(void* state_args) {
struct state2_args* args = (struct state2_args*)state_args;
use(args->third);
...
}
// state pointer
void (*current_state)(void*);
// state logic
if (condition1) {
current_state = &state_1;
current_state_args = &args_1;
} else if (condition2) {
current_state = &state_2;
current_state_args = &args_2;
}
// do the thing
(*current_state)(current_state_args);
Is it possible to have a function in C return a 'dynamic' return type
example
printResult (NumOrChar());
void* NumOrChar(void) {
// return int or char
}
void printResult (void* input) {
if (isdigit(input)) {
printf("It's a number");
}
else {
printf("It's not a number");
}
Functions can certainly return void *. But that is a specific pointer type with properties that make it suitable for conveying pointers to objects of any type. It is not a general-purpose wildcard type. Moreover, it does not carry any kind of information about the actual type, if any, of the object to which it points, so there is no way to determine that type dynamically. A C++ programmer might describe this situation as C not providing any RTTI.
Instead, you can return a type that can convey objects of a variety of types, known in advance, with a mechanism to discriminate among those. For example,
union num_or_string {
struct { _Bool is_num; };
struct { _Bool _x1; int num; };
struct { _Bool _x2; char *string; };
};
union num_or_string NumOrChar(void) {
// return a union num_or_string containing an int or a char *
}
void printResult (union num_or_string) {
if (num_or_string.is_num) {
printf("It's a number: %d\n", num_or_string.num);
} else {
printf("It's a string: %s\n", num_or_string.string);
}
}
You can use _Generic in some circumstances
int func_int(int *p)
{
printf("%s\n", __FUNCTION__);
return 5; /* does not make too much sense */
}
float func_float(float *p)
{
printf("%s\n", __FUNCTION__);
return 5.0f; /* does not make too much sense */
}
double func_double(double *p)
{
printf("%s\n", __FUNCTION__);
return 5.0; /* does not make too much sense */
}
#define func(p) _Generic((p), \
int *: func_int, \
float *: func_float, \
double *: func_double)(p) \
Instead of having a block of ifs, one can use a structure of function pointers as a virtual table, including to_string. The following dynamically creates the Type, which can be Num or Letter.
#include <stddef.h> /* offsetof */
#include <stdio.h> /* [|s|sn]printf, fgets, stdin */
#include <stdlib.h> /* malloc, free, strtol */
#include <ctype.h> /* isdigit */
#include <errno.h>
#include <assert.h>
struct Type;
typedef void (*TypeToString)(const struct Type *const, char (*const)[32]);
typedef void (*TypeAction)(struct Type *const);
struct Type {
const struct TypeVt *vt;
};
/* Num extends Type. */
struct Num {
struct Type base;
int value;
};
static struct Num *num_upcast(struct Type *const type) {
return (struct Num *)(void *)((char *)type - offsetof(struct Num, base));
}
static const struct Num *const_num_upcast(const struct Type *const type) {
return (const struct Num *)(const void *)((const char *)type
- offsetof(struct Num, base));
}
static void num_to_string(const struct Type *const type, char (*const a)[32]) {
const struct Num *const num = const_num_upcast(type);
snprintf(*a, sizeof *a, "%d", num->value); /* C99. */
}
static void num_delete(struct Type *const type) {
struct Num *const num = num_upcast(type);
free(num);
}
/* Letter extends Type. */
struct Letter {
struct Type base;
char letter;
};
static struct Letter *letter_upcast(struct Type *const type) {
return (struct Letter *)(void *)((char *)type
- offsetof(struct Letter, base));
}
static const struct Letter *const_letter_upcast(const struct Type *const type) {
return (const struct Letter *)(const void *)((const char *)type
- offsetof(struct Letter, base));
}
static void letter_to_string(const struct Type *const t, char (*const a)[32]) {
const struct Letter *const letter = const_letter_upcast(t);
sprintf(*a, "%c", letter->letter);
}
static void letter_delete(struct Type *const type) {
struct Letter *const letter = letter_upcast(type);
free(letter);
}
static const struct TypeVt {
const char *name;
const TypeToString to_string;
const TypeAction delete;
} num_vt = { "num", &num_to_string, &num_delete },
letter_vt = { "char", &letter_to_string, &letter_delete };
static void type_to_string(const struct Type *const t, char (*const a)[32]) {
assert(t);
t->vt->to_string(t, a);
}
static void type_delete(struct Type *const t) {
assert(t);
t->vt->delete(t);
}
static struct Type *num(const int value) {
struct Num *num = malloc(sizeof *num);
if(!num) return 0;
num->base.vt = &num_vt;
num->value = value;
return &num->base;
}
static struct Type *letter(const char letter) {
struct Letter *l = malloc(sizeof *l);
if(!l) return 0;
l->base.vt = &letter_vt;
l->letter = letter;
return &l->base;
}
static struct Type *read_type(void) {
struct Type *type;
char buffer[64];
if(!fgets(buffer, sizeof buffer, stdin)) return 0;
if(isdigit(buffer[0])) {
long n;
errno = 0;
n = strtol(buffer, 0, 0);
if(errno) return 0;
type = num(n);
} else {
type = letter(buffer[0]);
}
return type;
}
int main(void) {
char a[32];
struct Type *type = 0;
int is_success = 0;
do {
if(!(type = read_type())) break;
type_to_string(type, &a);
printf("\"%s\" is of type %s.\n", a, type->vt->name);
is_success = 1;
} while(0); {
if(type) type_delete(type);
}
if(!is_success) return perror("Failure"), EXIT_FAILURE;
return EXIT_SUCCESS;
}
Probably overkill for your function, but as one has more types, this becomes increasingly attractive. One can consider a union of similar spaced types so that it could be allocated entirely on the stack.
$ bin/numorchar
524645 3456542563456
"524645" is of type num.
$ bin/numorchar
6245635724564357652654245634576
Failure: Result too large
$ bin/numorchar
ata gfddsgsdg
"a" is of type char.
$ bin/numorchar
"
" is of type char.
I guess you are speaking about the C# feature (according to my Google search).
In C, it's not possible unless doing it yourself (other answers show examples). It can be easy or hard depending of your needs. You should think about switching to another language if you really want this (they are sometimes called variants in them).
I passed a void pointer to a function.
Since the evaluation of the function differs a little between int and char, I want to determine in the function whether I get an int or char or another value passed.
How can I do this without the programmer calling this function having to pass a further variable to tell the function what value it is processing.
Vor Exempel:
int func (void *value) {
if (int) {
//Process
}
if else (char) {
//Process
}
else {
//Failure
}
How can I do this without the programmer calling this function having to pass a further variable to tell the function what value it is processing(?)
If you can pass something other than only void*, use _Generic (since C11) to unburden the caller and steer code to call the needed function. This approach uses a macro to steer the code, akin to #tadman comment.
void func_int(int x) {
printf("int: %d\n", x);
}
void func_char(char x) {
printf("char: %c\n", x);
}
#define func(X) _Generic((X), \
int: func_int, \
char: func_char \
)(X)
Usage. No need for the caller to specify one of the implemented types: int, char. Just use func().
int main() {
int i = 42;
char c = 'X';
func(i);
func(c);
}
Output
int: 42
char: X
You can not, void * is generic and there is no way to know the original type, consider using an union
enum type {TYPE_INT, TYPE_CHAR);
struct mytype {
enum type;
union {
int *as_int;
char *as_char;
} data;
};
and pass it to the function:
int func(struct mytype *mydata) {
if (mydata->type == TYPE_INT) {
return *(mydata->data.as_int);
} else {
//Process
}
...
Under C11 you can use anonymous unions, then you can ommit the union name:
struct mytype {
enum type;
union {
int *as_int;
char *as_char;
};
};
...
if (mydata->type == TYPE_INT) {
return *(mydata->as_int);
I have two modules written in C11 in my project: 'test1.c' and 'test2.c'.
Module 'test1.c':
int FunctionWithVeryLONGLONGLONGLONGName(char* data)
{
// do something
}
Module 'test2.c':
extern int FunctionWithVeryLONGLONGLONGLONGName(char* data);
int main(void)
{
char data[ DATA_LEN + 1 ] = { "test_data" };
FunctionWithVeryLONGLONGLONGLONGName(data);
return 0;
}
I want to use short name for function 'FunctionWithVeryLONGLONGLONGLONGName' in module 'test2.c' without modification of module 'test1.c'. F.e., something like this:
FuncWithShortName(data);
I try to do:
extern int FunctionWithVeryLONGLONGLONGLONGName(char* data);
typedef int FunctionWithVeryLONGLONGLONGLONGName(char* data);
FunctionWithVeryLONGLONGLONGLONGName FuncWithShortName;
int main(void)
{
char data[ DATA_LEN + 1 ] = { "test_data" };
FuncWithShortName(data);
return 0;
}
But compiler gave an error:
"Definition of function FunctionWithVeryLONGLONGLONGLONGNamerequires parentheses."
What did I do wrong ?
typedef creates a type alias, not a function alias or anything else.
You could get a sort of function alias by defining a file-local pointer to function:
static int (*const FuncWithShortName)(char*) =
FuncWithVeryLONGLONGLONGLONGName;
First solution:
struct str {
char *name;
int flag;
};
enum {
HELP,
OUTPUT,
OTHER
};
typedef struct str table;
table arr[] = {
{ "help", HELP },
{ "output", OUTPUT },
{ NULL, OTHER },
};
int main(int argc, char **argv) {
table *opt = arr;
printf("%s\n", (opt+HELP)->name);
printf("%s\n", (opt+OUTPUT)->name);
return 0;
}
Second solution:
struct str {
char *name;
int flag;
};
enum {
HELP,
OUTPUT,
OTHER
};
typedef struct str table;
table arr[OTHER];
void start_table() {
arr[HELP] = (struct str) { "help", HELP };
arr[OUTPUT] = (struct str) { "output", OUTPUT };
arr[OTHER] = (struct str) { NULL, OTHER };
}
int main(int argc, char **argv) {
start_table();
table *opt = arr;
printf("%s\n", (opt+HELP)->name);
printf("%s\n", (opt+OUTPUT)->name);
return 0;
}
What are the best? Second solution change automatically if I add or change any element of the array, but is efficient? Is the best enumerate or using the #define preprocessor directive?
It depends!
When initialization can be used, use it — so the first solution is often better, especially if the code never changes the structure (array) contents.
If your code can change the contents but might need to be able to reset to the original state, then the initialization function becomes more appropriate and using direct initialization is not appropriate.
There is no one 'best' solution; what is best depends on what you need to do with the array.
Using a C99 specific intializer syntax, you can get the advantages of the second approach in a static initializer:
#include <stdio.h>
struct str {
const char *name;
int flag;
};
enum {
HELP,
OUTPUT,
OTHER,
};
typedef struct str table;
table arr[] = {
[HELP] = { "help", HELP },
[OUTPUT] = { "output", OUTPUT },
[OTHER] = { NULL, OTHER },
};
int main(int argc, char **argv) {
table *opt = arr;
printf("%s\n", opt[HELP].name);
printf("%s\n", opt[OUTPUT].name);
return 0;
}
The advantage of this technique is it makes no assumption on the order of the enum values, nor on their actual values as long as they are positive, distinct and reasonably small.