Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to lift a function to take an extra parameter in C?

(I understand that C is not intended to be used in a functional way. However, having learned functional programming, I have trouble thinking differently.)

Given those restrictions:

  • No nested functions because clang forbids it (so no lamda expressions)
  • no global variables

Can we think of a way of lifting a function f to take an extra parameter to fit a prototype, such as this new parameter will be ignored when f is executed?

Here is in detail what I want to do:

I want to fit a function f whose type is:

f :: void f(char *s)

in a function g that takes an function as an argument (call it arg), whose type is:

arg :: void f(unsigned int i, char *s)

Thus, the type of g is:

g :: void g(void (*f) (unsigned int, char))

The solution in haskell would be the following:

g (const f)

Is that even possible, maybe with some kind of macro wizardry?

EDIT: To provide a better understanding, here is the actual code. The body of ft_striter needs to be completed. The purpose is: apply the function f to every character of a string, using or not using its index i.

void        ft_striter(char *s, void (*f) (char *s))
{
    ft_striteri(?);
}

static void ft_striteri_helper(char *s, unsigned int i, void (*f)(unsigned int, char*))
{
    if (*s) 
    {
        f(i, s);
        ft_striteri_helper(s + 1, i + 1, f);
    }
}

void        ft_striteri(char *s, void (*f)(unsigned int, char*))
{
    ft_striteri_helper(s, 0, f);
}
like image 770
qleguennec Avatar asked Dec 07 '15 16:12

qleguennec


2 Answers

You cannot implement this in standard portable C. The problem is that the function g. is designed wrong. It should have type

void g(void (*f) (void *, unsigned int, char), void *ctxt)

and it should pass its ctxt argument as the first argument to any f calls it makes.

Now you can implement what you want with code like

struct const_ctxt {
    void (*fun)(char *);
}

void const(void *ctxt, unsigned int i, char *s)
{
    ((struct const_ctxt *)ctxt)->fun(s);
}

void call_g_using_const_f(void (*f)(char *))
{
    struct const_ctxt *ctxt = malloc(sizeof (struct const_ctxt));
    ctxt->fun = f;
    g(const, (void *)ctxt);
    free(ctxt);
}

Warning: If the arguments to g might escape the dynamic scope of g then you will need to find another strategy for managing the allocation of the ctxts.

The pattern of g taking a pair of a code pointer and a data "context"/"environment" pointer is how higher-level languages typically implement closures.

like image 190
Reid Barton Avatar answered Oct 19 '22 12:10

Reid Barton


Since you want only to accept and ignore the extra parameter, you can accomplish this by creating a wrapper function with the required signature, that simply delegates to the original function:

void f2(unsigned int i, char *s) {
    f(s);
}

With a proper declaration of that function in scope, you can then simply call

g(f2);

Function f2() can be declared static if you wish, so that it is not visible to code outside the file in which it is defined. It does not need to be nested.

like image 43
John Bollinger Avatar answered Oct 19 '22 11:10

John Bollinger