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.

5
  • 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? Commented Aug 8, 2014 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. Commented Aug 8, 2014 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? Commented Aug 8, 2014 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. Commented Aug 8, 2014 at 23:26
  • It seems that it is not possible to make a curry functions using pure C. Commented Aug 9, 2014 at 1:59

4 Answers 4

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);
Sign up to request clarification or add additional context in comments.

5 Comments

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, Unfortunately, C does not natively support closures. I think the newer standard for C++ does, so you may want to consider that.
@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: 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 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.
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.

2 Comments

Thanks for the advice. While I am not explicitly coding for multiple-threads, I definitely see how this could be a problem.
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 :).
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.

Comments

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;

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.