6

(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);
}
qleguennec
  • 544
  • 4
  • 12
  • Is `f` fixed or variable? – fuz Dec 07 '15 at 16:52
  • One way of answering that question would be to somehow find a way to allow f to be visible to arg, to be able to call f in its function body. – qleguennec Dec 07 '15 at 16:54
  • f is a variable relative to g – qleguennec Dec 07 '15 at 16:56
  • @qleguennec, no, [I'm wrong](http://ideone.com/NWmtB4). – effectfully Dec 07 '15 at 17:01
  • @user3237465 I'll re-edit, but whatever, haskell is not really a thing there – qleguennec Dec 07 '15 at 17:05
  • With the restrictions you impose, you can't, but in practice it's not a problem because the restrictions you impose do not exist. I cannot provide a proof for this, though, so just a comment instead of an answer. – fuz Dec 07 '15 at 17:09
  • @FUZxxl They do exist if you say they do, or, in my case, someone else say they do. I should have been downvoted for that question. – qleguennec Dec 07 '15 at 17:12
  • 2
    @qleguennec Then you made probably a mistake when you designed your callback. If you could post your actual code, we can try to find a solution for that instead of solving a meta-problem that cannot be solved in general in C. – fuz Dec 07 '15 at 17:14
  • @qleguennec, you seem to have created an unnecessary problem for yourself. Especially with such a simple function `ft_striteri_helper()`, the easiest solution would probably be to write a separate version of that function for the case of one-arg `f()`. A C++ solution would undoubtedly boil down to that once you pierced the layers of polymorphism and encapsulation that surely would be applied. You could write C code that emulated the C++ way, but that seems like rather a lot of mess and fuss for something that can be simple. – John Bollinger Dec 07 '15 at 17:29
  • @John Bollinger I do not chose the types of the functions I need to write, nor the language I use to write them. – qleguennec Dec 07 '15 at 18:08
  • @qleguennec, to a large extent, you *do* choose the types of functions you write. In this case, the alternative I suggested seems entirely viable, as it would not need to involve any differences at all in the names or arguments of the external functions you provide. – John Bollinger Dec 07 '15 at 19:21

3 Answers3

4

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.

Reid Barton
  • 14,951
  • 3
  • 39
  • 49
  • This might be the closest answer, but unfortunatly, this can't work because the prototypes are immuable, it has to be exactly as stated. I will however validate that answer. – qleguennec Dec 07 '15 at 18:06
  • @qleguennec In C, well-designed libraries involving callbacks often use this kind of idiom to get something similar to functional closures. A closure can be implemented as a fully-abstracted lambda term (with no free variables) paired with an environment (providing the values for the first `k` arguments). In C, this is rendered as a function pointer, plus a `void *` pointer to the environment/context. If a library fails to provide this feature, there's no portable workaround. Even the standard library fails at this (`qsort`), though :-/ – chi Dec 07 '15 at 19:56
  • This will probably upset you and I am sorry about it. After reading documentation I understood that solution and this is the right answer to my problem. However, and I recognize I didn't state it in my post, I am not allowed to declare a structure nor a typedef in a c file. I could declare the structures I need in my header, but then I'd need to find a name to all of them. I guess I can't use this solution, but thanks anyway, I learned something! – qleguennec Dec 07 '15 at 21:20
2

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • f is not visible to that function you wrote – qleguennec Dec 07 '15 at 17:04
  • @qleguennec, whyever not? Surely whether `f()` is visible to `f2()` is not directly dependent on `f2()` itself. – John Bollinger Dec 07 '15 at 17:06
  • @qleguennec That makes no sense. Maybe you should edit your question and provide skeleton C program to demonstrate the problem. – user694733 Dec 07 '15 at 17:07
  • Because f is relative to g, even if it is defined somewhere else, at any point, I'll use it like this: g(that_function). However, I'll refer it as 'f' in the body of g. If you think it will make more sense, I can edit my question with the real code I'm trying to write. – qleguennec Dec 07 '15 at 17:10
-1

Well let's consider a few options.

Firstly C has no lambda expressions, although the latest C++ standard does. It might be worth your while consider C++ instead of C if this is a major issue for you.

C can define and pass function pointers. So it is perfectly legal to pass a function as a parameter to another function.

You can also define functions to take a variable number of arguments, which may also be useful to you.

Also C macros can be useful. You can, for example do this :

#define f1( x )   g( (x), 1 )
#define f2( x )   g( (x), 2 )

int g( int x, int y )
{
    return ( x + y ) ;
}

You can even do clever things with macros to allow macro overloading. That's not exactly ideal in terms of coding style, but it's possible.

So there may be some tools in there to get you what you want.

Update

The OP added an update to his post while I was making up mine, so if I understand his goal here is a rough suggestion.

As it appears the OP wants the chosen function to have access to an index variable which can be set elsewhere he could try using a struct for parameters. Perhaps something like :

typedef struct mystring_s {
    char *s ;
    int i ;
    } mystring_t ;

void f1( mystring_t *strp )
{
    /* can access strp->s and strp->i */
}

/* and similar for other functions f2, f2, etc. */

/* And to use this you can call any function using
 */

void g( void (*fn)( mystring_t * ), mystring_t *strp )
{
    (*fn)( strp ) ;
}

void set_i( mystring_t *strp, int v )
{
    strp->i = v ;
}

/* for example */

mystring_t s ;

/* set up s */

set_i( &s, 11 ) ;
g( &f1, &s ) ;
set_i( &s, 31 ) ;
g( &f2, &s ) ;

Of course he could also try using a global value ( which he apparently does not want to or thinks is not possible ), and he could even store a function pointer with the data.

Hard to know for sure if this is a workable idea for the OP, as he's really trying to do something that C is not designed for. I think the problem is that he has a design idea in his head and it is a bad choice for C. It might be better to look at how this requirements arose, and modify that overall design to something more C-friendly, than to try and make C do something to replace Haskell's capabilities.

Community
  • 1
  • 1
  • Varargs functions are irrelevant to the problem as stated because the functions involved *are not* varargs functions. No amount of wrapping, via varargs or otherwise, allows a function pointer of type `void(*)(unsigned int, char *)` to carry information about what *other* function the pointed to function should call. That has to be determined somehow by the function implementation. – John Bollinger Dec 07 '15 at 17:36
  • Macros are not useful for the OP's case, because he wants a *dynamic* solution, not a static one. – John Bollinger Dec 07 '15 at 17:37
  • Possibly C++ and lambdas would be an alternative, but the OP has asserted that it is not. Unless you have some reason to contradict him on that point, it is useless to suggest lambdas (or C++). – John Bollinger Dec 07 '15 at 17:40