4

In C, I am trying to pass a single-variable function into an optimization routine (optimization_routine). The optimization routine takes as input a pointer func1ptr to a function of a single float variable. However, I need to be able to pass multiple variables into this function. Thus, I am trying to construct a function pointer of one variable where all but the first inputs are "constants" into the function variable (sort of analogous to a partial derivative in calculus). I think I can do this with function pointers, but I can't figure out a syntax that makes sense.

That is, I have a function like this:

float function_all_inputs( float A, int B, float C, char D);

The optimization function requires a pointer like this:

typedef (*func1ptr)(float);
void optimization_function( func1ptr fp );

Thus, I want to construct a function of this form:

// create a function of A only at runtime using inputs B,C,D
func1ptr fp = ( & function_all_inputs(A,B,C,D))(A);  

The function pointed to by fp should have the signature:

float function_one_input(float A);

Inputs B, C, and D are calculated elsewhere in the code, and thus are not known at compile-time; however, they are constant inside optimization_function.

I think I can do this in pure C using function pointers, however, I can't figure out the correct syntax. None of the examples I found online cover this case. Any advice you can provide would be appreciated.

user1004061
  • 103
  • 1
  • 9
  • Does `optimization_function()` synchronously call your function that you pass in, or does it stash it away someplace and call it later? If you call `optimization_function()` more than once, does it want to remember each function you pass in, or does it only remember the last one? – jxh Aug 08 '14 at 21:51
  • I don't fully understand your question, but this might help: `optimization_function` calls the function pointed to by `fp` multiple times. Each time `optimization_function` calls `fp`, it is with a different value of A. – user1004061 Aug 08 '14 at 23:03
  • If you had a print function in the function you pass to `optimization_function()`, would you see the print output come out before `optimization_function()` returns, or would you see the print output sometime later? – jxh Aug 08 '14 at 23:07
  • Yes you would see the print statement before 'optimization_function()` returns (in fact, you would see it several times, since `optimization_function` calls the `fp` function several times). I've confirmed this is the case. – user1004061 Aug 08 '14 at 23:26
  • It seems that it is not possible to make a curry functions using pure C. – BLUEPIXY Aug 09 '14 at 01:59

4 Answers4

4

It sounds like you are asking how to create a closure to capture parameters in C, and you can take a look at some options in the linked question.

However, without custom extensions, I think you will need to use global variables to achieve the effect you are looking for.

// Pass this wrapper with the name "wrapper" into the function 
// that requires a function pointer
void wrapper(float a) {
    // Where last four arguments are global variables that are computed first.
    function_all_inputs(a, b, c, d, e); 
}

// No need to create an explicit function pointer. 
// Passing the name of the function is sufficient.
optimization_function(wrapper);
Community
  • 1
  • 1
merlin2011
  • 71,677
  • 44
  • 195
  • 329
  • Hmm, thanks for the advice. "Closure" was the word I was looking for--I've seen this word many times but did not know what it meant. I'd like to avoid global variables if possible, as I plan to make this into a DLL. – user1004061 Aug 08 '14 at 21:35
  • @user1004061, Unfortunately, C does not natively support closures. I think the newer standard for C++ does, so you may want to consider that. – merlin2011 Aug 08 '14 at 21:37
  • @user1004061, One way to reduce the namespace pollution is to use the `static` keyword to at least make the global variables only visible within the current compilation unit. – merlin2011 Aug 08 '14 at 21:38
  • 1
    @merlin2011: Yes, the newer C++ standard supports closures, called "Lambda's", though they are only convertible to function-pointers if they are only plain functions (no data). So, no advantage to be had there. – Deduplicator Aug 08 '14 at 21:39
  • @Deduplicator there is still an advantage because if you switch from function pointers to function objects (ah la `std::function`) you can pass in lambda expressions with data. – RamblingMad Aug 09 '14 at 01:10
0

You need to write a wrapper function, like

int b;
float c;
char d;
int wrap(float a) {
    return function_all_inputs(a, b, c, d);
}

Consider concurrency an re-entrancy though:

If multiple threads can use the wrapper, and need it to pass different data, make those globals thread-local:

_Thread_local int b;

If you need full re-entrancy, things get complicated:

You need to (also) save the variables before using a nested invocation with different parameters.
Writing a second (and maybe third) version of the wrapper using different globals may be better.

If you need more active at the same time, you can try a pool of those functions, though it gets unwieldy really fast. Better change your optimization-function by adding a context-parameter, and pass those extra-parameters with that.

For full freedom, you really need a way to write functions at runtime, at least enough to recover a context-pointer. That's not possible in pure C though.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • Thanks for the advice. While I am not explicitly coding for multiple-threads, I definitely see how this could be a problem. – user1004061 Aug 08 '14 at 21:36
  • I kind-of knew the writing functions at runtime was not possible. I'm used to interpretive languages, so I'm still getting used to these restrictions in C :). – user1004061 Aug 08 '14 at 21:38
0

If sizeof(float) >= sizeof(void*) on your platform, then you can "hack" it as follows:

typedef struct
{
    float a;
    int   b;
    float c;
    char  d;
}
params;

int function_all_inputs(float a, int b, float c, char d)
{
    ...
}

int function_one_input(float f)
{
    params* p;
    memcpy((void*)&p, (void*)&f, sizeof(void*));
    return function_all_inputs(p->a, p->b, p->c, p->d); 
}

int optimize()
{
    float   f;
    params  v;
    params* p = &v;

    v.a = ...;
    v.b = ...;
    v.c = ...;
    v.d = ...;

    memcpy((void*)&f, (void*)&p, sizeof(void*));
    return optimization_function(function_one_input, f);
}

You weren't very consistent in your question about the return-value type, so I used int.

barak manos
  • 29,648
  • 10
  • 62
  • 114
0

This may be overkill, but libffi supports creating closures in the following way:

#include <stdio.h>
#include <ffi.h>

typedef struct BCD { int B; float C; char D; } BCD;

void function_one_input_binding
  (ffi_cif* cif, int* result, void** args, BCD* bcd) {
  *result = function_all_inputs(*(float*)args[0], bcd->B, bcd->C, bcd->D);
}

int main() {

  ffi_cif cif;
  ffi_type* args[1];
  ffi_closure* closure;

  int (*function_one_input)(float);

  // Allocate a closure.
  closure = ffi_closure_alloc(sizeof(ffi_closure), &function_one_input);

  // Tell libffi the parameter and return types.
  args[0] = &ffi_type_float;
  ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_int, args);

  // Bind closure data.
  BCD bcd = { .B = 1, .C = 2.5, .D = 'x' };
  ffi_prep_closure_loc(
    closure, &cif, function_one_input_binding, &bcd, function_one_input);

  // Call the function.
  int result = function_one_input(42.5);

  // Free the allocated closure.
  ffi_closure_free(closure);

  return 0;

}
Jon Purdy
  • 53,300
  • 8
  • 96
  • 166