Is it possible to build functions within a function in C? - c

For a class assignment we are writing a program which takes in input, and sorts each line of input with qsort with strcmp as its comparator function. Because the type of strcmp is not the same as required by qsort, a wrapper function needs to be built to satisfy qsort.
I wasn't satisfied with this, and wanted to write a function which takes in a function of type:
int (*cmp)(const char *, const char *)
and returns a function of type:
int (*qsortcmp)(const void *, const void *)
Is this possible? Have I been writing too much haskell? I would like to know.

C does not support lambda functions. Your only choice is to create a function of the type expected by qsort and call strcmp from inside of that function.
For example:
int qsort_cmp_str(const void *a, const void *b)
{
const char *s1 = a;
const char *s2 = b;
return strcmp(s1, s2);
}

It’s possible only with a global variable that holds the real function to call. (Yes, that’s horrible.)
There is a reason: a function pointer with only the obvious parameters cannot represent a closure. Had qsort accepted a pointer of type
int (*)(void *closure, const void*, const void*)
and a void* to pass it for each comparison, it would be possible to write a converter:
struct char_qsorter {
int (*function)(void*, const char*, const char*);
void *param;
};
struct qsorter {
int (*function)(void*, const void*, const void*);
void *param;
};
int char_qsort_wrapper(void *closure, const void *a, const void *b) {
const char_qsorter *const cq=closure;
return cq->function(cq->param, a, b);
}
qsorter convert(const char_qsorter *cq) {
const qsorter ret={char_qsort_wrapper, cq};
return ret;
}
/* Convert a plain function pointer: */
int trivial_wrapper(void *closure, const char *a, const char *b) {
return (*(int (*const *)(const char*, const char*))closure)(a, b);
}
char_qsorter trivial_closure(int (*const *f)(const char*, const char*)) {
const char_qsorter ret={trivial_wrapper, f};
return ret;
}
void sort_strings(const char *const *ss, size_t n) {
int (*const f0)(const char*, const char*)=strcmp;
const char_qsorter cq=trivial_closure(&f0);
const qsorter q=convert(&cq);
qsort_closure(ss, n, sizeof(*ss), q.function, q.param);
/* or just pass q if the library used the struct */
}
I think it’s obvious why in practice people prefer the hardcoded solution where it’s sufficient. (Really, lazy programmers just pass strcmp directly, which works on most implementations.)
C11 does support this with qsort_s, but it’s a special optional function with weird runtime error checking added on.

Related

C [-Wincompatible-pointer-types] how to cast

my code: https://godbolt.org/z/de7fbdjh7
code from source: https://stackoverflow.com/a/49072888/15603477
Almost exact the same.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
int iValue;
int kValue;
char label[6];
} my_data;
int cmp_mydata_ivalue(my_data* item1 , my_data* item2 )
{
if(item1->iValue < item2->iValue) return -1;
if(item1->iValue > item2->iValue) return 1;
return 0;
}
int main(void){
my_data datalist[256] = {0};
{
int i;
for(i = 0;i<20;i++){
datalist[i].iValue = i+100;
datalist[i].kValue = i+1000;
sprintf(datalist[i].label,"%2.2d", i+10);
}
}
printf("new line\n");
{
my_data srchitem = {105,1018,"13"};
my_data *foundItem = (my_data*) bsearch(&srchitem, datalist,20, sizeof(my_data),cmp_mydata_ivalue);
bsearch_results(&srchitem, foundItem);
}
}
The same question asked many times. But I don't know how to cast it.
error code:
*callback1.c: In function ‘main’:
callback1.c:58:89: warning: passing argument 5 of ‘bsearch’ from incompatible pointer type [-Wincompatible-pointer-types]
58 | my_data *foundItem = (my_data*) bsearch(&srchitem, datalist,20, sizeof(my_data),cmp_mydata_ivalue);
| ^~~~~~~~~~~~~~~~~
| |
| int (*)(my_data *, my_data *) {aka int (*)(struct <anonymous> *, struct <anonymous> *)}*
One way to try to use gcc option to supress the error. Another way is somewhere I need to cast. But now i don't know how to cast.
Tutorial I found so far: https://www.tutorialspoint.com/c_standard_library/c_function_bsearch.htm
The comparison function must have the type
int ( const void *, const void * )
See the declaration of the function bsearch
void *bsearch(const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
So you should declare and define your function like
int cmp_mydata_ivalue( const void *a , const void *b )
{
const my_data *item1 = a;
const my_data *item2 = b;
if ( item1->iValue < item2->iValue) return -1;
if(item1->iValue > item2->iValue) return 1;
return 0;
}
Don't ever use workarounds to suppress errors/warnings from the compiler. You should carefully understand them and fix the code instead. If you chose to ignore them, you must be very conscious of what are the implications.
Having said that, the bsearch prototype is the following:
void* bsearch( const void *key, const void *ptr, size_t count, size_t size,
int (*comp)(const void*, const void*) );
meaning it expects the last parameter to be a function pointer to a function with the following signature:
int function(const void*, const void*);
What you are passing is a function of this kind
int cmp_mydata_ivalue(my_data* item1 , my_data* item2 )
Which is uncompatible, as C can't do any implicit cast. You must rewrite your function to something like this:
int cmp_mydata_ivalue( const void *cvp_item1 , const void *cvp_item2 )
{
const my_data *item1 = (const my_data *)cvp_item1;
const my_data *item2 = (const my_data *)cvp_item2;
}

C type won't be recognized properly

I have an error when I compile my C program.
I have this code :
#include <stdio.h>
#include <stdlib.h>
#define SET__SIZE 10
#define SET__BOUND ((void*) NULL)
struct set {
void *s[SET__SIZE];
int cursor;
int (*cmp)(const void*, const void*);
void * (*copy)(const void*);
void (*del)(void *);
};
int find(const void *s[], void *c, int (*cmp)(const void*, const void*))
{
int i = 0;
while (s[i]!=SET__BOUND && cmp(s[i],c)<0)
i++;
return i;
}
int set__find(const struct set *se, void *c)
{
return (se->cmp(se->s[find(se->s,c,se->cmp)],c)==0);
}
For some reason, gcc is raising a warning for the find call in set__find saying :
note: expected ‘const void **’ but argument is of type ‘void * const*’
I can't understand why he thinks the argument is a constant pointer (if I understood the error right)
As I tried, the note goes away, when I change the code to
struct set {
void *s[SET__SIZE];
int cursor;
int (*cmp)(const void*, const void*);
void * (*copy)(const void*);
void (*del)(void *);
};
int find(void * const s[], void *c, int (*cmp)(const void*, const void*))
{
int i = 0;
while (s[i]!=SET__BOUND && cmp(s[i],c)<0)
i++;
return i;
}
int set__find(const struct set *se, void *c)
{
return (se->cmp(se->s[find(se->s,c,se->cmp)],c)==0);
}
Explanation
In the answer... No point of writting it again.
why he thinks the argument is a constant pointer
struct set {
void *s[SET__SIZE];
The structure set contains the array s.
const struct set *se
se points to a constant structure set. Because the structure is constant, the memory for the structure is constant. The elements of the array s can't be modified, they are in constant memory.
find(se->s,
Arrays decay to the pointer to the first element. So imagine it's TYPE s[SET__SIZE] where TYPE is a void*. TYPE s[SET__SIZE] decays to a pointer TYPE *. But, it's constant, so it's const TYPE s[SET__SIZE]. So it decays to a const TYPE *. You can't modify it, it's a constant array. TYPE is a void* - you can dereference the element and modify it then, but you can't modify the pointer value itself.
TL;DR you want int find(void * const s[] as in the other answer.

Pointer to a function that accepts both const and non-const arguments

I want to write a wrapper for read and write unix functions, but read has a const void pointer parameter, and write a simple void pointer as a parameter.
So, a prototype like this, will fail for one of the functions:
typedef ssize_t (*genericStreamHandler)(int, const void*, size);
Do not prototype the function signature if code needs to allow incompatible functions.
The following compiles without warnings/errors.
#include <stdio.h>
ssize_t file_read(int h, const void* b, int sz) {
if (b) return 0;
return h + sz;
}
ssize_t file_write(int h, void* b, int sz) {
if (b) return 0;
return h + sz;
}
//typedef ssize_t (*genericStreamHandler)(int, void*, int);
// v--- No function prototype
typedef ssize_t (*genericStreamHandler)();
int main(void) {
genericStreamHandler gFH1 = file_read;
genericStreamHandler gFH2 = file_write;
char buf[10];
return (*gFH1)(0, buf, 10) + (*gFH2)(0, buf, 10);
}
OTOH, the better answer may lie in taking another approach that does not need a common type for variant function signatures.

is this a void pointer? casting? what is it doing?

I am new to the C language and pointers and I am confused by this function declaration:
void someFunction(int (*)(const void *, const void *));
Can anyone explain in layman's terms what this does and how it works?
It's the prototype of a function that takes:
a pointer to a function that takes a const void* and a const void* as arguments and returns an int
as an argument, and returns void.
It declares a function, which takes another function as its argument, and returns nothing. The other function would be declared as
int otherfunction( const void *, const void * );
and you would call somefunction() like this:
somefunction( otherfunction );
It's a function that has a single parameter. That parameter is a pointer to a function that returns an int and takes those two void pointers to constant data parameters.
This is the declaration of a function which takes a function pointer as its argument. In its most basic form, it looks like this:
void someFunction(argument_type);
Where argument_type is int (*)(const void *, const void *), which can be described as a "pointer to a function that takes two const void * arguments, and returns an int". i.e. any function that has the following declaration:
int foo(const void *, const void *);
To illustrate by example:
int foo_one(const void * x, const void * y) { ... }
int foo_two(const void * x, const void * y) { ... }
void someFunction(int (*)(const void *, const void *) function_ptr)
{
const void * x = NULL;
const void * y = NULL;
int result;
result = (*function_ptr)(x, y); // calls the function that was passed in
}
int main()
{
someFunction(foo_one);
someFunction(foo_two);
return 0;
}
Check this very helpful when dealing with complex declarations.

Warning when using qsort in C

I wrote my comparison function
int cmp(const int * a,const int * b)
{
if (*a==*b)
return 0;
else
if (*a < *b)
return -1;
else
return 1;
}
and i have my declaration
int cmp (const int * value1,const int * value2);
and I'm calling qsort in my program like so
qsort(currentCases,round,sizeof(int),cmp);
when i compile it I get the following warning
warning: passing argument 4 of ‘qsort’ from incompatible pointer type
/usr/include/stdlib.h:710: note: expected ‘__compar_fn_t’ but argument is of type ‘int
(*)(const int *, const int *)’
The program works just fine so my only concern is why it doesn't like the way im using that?
The cmp function's prototype must be
int cmp(const void* a, const void* b);
You can either cast it in the invocation of qsort (not recommended):
qsort(currentCases, round, sizeof(int), (int(*)(const void*,const void*))cmp);
or casts the void-pointers to int-pointers in cmp (the standard approach):
int cmp(const void* pa, const void* pb) {
int a = *(const int*)pa;
int b = *(const int*)pb;
...
According to the man page, a __compar_fn_t is defined as: typedef int(*) __compar_fn_t (const void *, const void *)
Your cmp specifies int* parameters. It doesn't like that, but is only listed as a warning.

Resources