Question

Dearest stack exchange,

I'm programming an MRI scanner. I won't go into too much background, but I'm fairly constrained in how much code I've got access to, and the way things have been set up is...suboptimal. I have a situation as follows:

  • There is a big library, written in C++. It ultimately does "transcoding" (in the worst possible way), writing out FPGA assembly that DoesThings. It provides a set of functions to "userland" that are translated into (through a mix of preprocessor macros and black magic) long strings of 16 bit and 32 bit words. The way this is done is prone to buffer overflows, and generally to falling over.*
  • The FPGA assembly is then strung out over a glorified serial link to the relevant electronics, which executes it (doing the scan), and returning the data back again for processing.
  • Programmers are expected to use the functions provided by the library to do their thing, in C (not C++) functions that are linked against the standard library. Unfortunately, in my case, I need to extend the library.
  • There's a fairly complicated chain of preprocessor substitution and tokenization, calling, and (in general) stuff happening between you writing doSomething() in your code, and the relevant library function actually executing it. I think I've got it figured out to some extent, but it basically means that I've got no real idea about the scope of anything...

In short, my problem is:

  • In the middle of a method, in a deep dark corner of many thousands of lines of code in a big blob I have little control over, with god-knows-what variable scoping going on, I need to:
    • Extend this method to take a function pointer (to a userland function) as an argument, but
    • Let this userland function, written after the library has been compiled, have access to variables that are local to both the scope of the method where it appears, as well as variables in the (C) function where it is called.

This seems like an absolute mire of memory management, and I thought I'd ask here for the "best practice" in these situations, as it's likely that there are lots of subtle issues I might run into -- and that others might have lots of relevant wisdom to impart. Debugging the system is a nightmare, and I've not really got any support from the scanner's manufacturer on this.

A brief sketch of how I plan to proceed is as follows:

In the .cpp library:

/* In something::something() /*
/* declare a pointer to a function */
    void (*fp)(int*, int, int, ...);
/* by default, the pointer points to a placeholder at compile time*/
    fp = &doNothing(...);

...

/* At the appropriate time, point the pointer to the userland function, whose address is supplied as an argument to something(): /*
    fp= userFuncPtr;
/* Declare memory for the user function to plonk data into */ 
    i_arr_coefficients = (int) malloc(SOMETHING_SENSIBLE);
/* Create a pointer to that array for the userland function */ 
    i_ptr_array=&i_arr_coefficients[0];
/* define a struct of pointers to local variables for the userland function to use*/
 ptrStrct=createPtrStruct(); 

/* Call the user's function: */
fp(i_ptr_array,ptrStrct, ...);

CarryOnWithSomethingElse();

The point of the placeholder function is to keep things ticking over if the user function isn't linked in. I get that this could be replaced with a #DEFINE, but the compiler's cleverness or stupidity might result in odd (to my ignorant mind, at least) behaviour.

In the userland function, we'd have something like:

void doUsefulThings(i_ptr_array, ptrStrct, localVariableAddresses, ...) { 
    double a=*ptrStrct.a;
    double b=*ptrStrct.b;
    double c=*localVariableAddresses.c;
    double d=doMaths(a, b, c);
    /* I.e. do maths using all of these numbers we've got from the different sources */

    storeData(i_ptr_array, d);
    /* And put the results of that maths where the C++ method can see it */
}

...

something(&doUsefulThings(i_ptr_array, ptrStrct, localVariableAddresses, ...), ...); 

...

If this is as clear as mud please tell me! Thank you very much for your help. And, by the way, I sincerely wish someone would make an open hardware/source MRI system.

*As an aside, this is the primary justification the manufacturer uses to discourage us from modifying the big library in the first place!

Was it helpful?

Solution

You have full access to the C code. You have limited access to the C++ library code. The C code is defining the "doUsefullthings" function. From C code you are calling the "Something" function ( C++ class/function) with function pointer to "doUseFullThings" as the argument. Now the control goes to the C++ library. Here the various arguments are allocated memory and initialized. Then the the "doUseFullThings" is called with those arguments. Here the control transfers back to the C code. In short, the main program(C) calls the library(C++) and the library calls the C function.

One of the requirements is that the "userland function should have access to local variable from the C code where it is called". When you call "something" you are only giving the address of "doUseFullThings". There is no parameter/argument of "something" that captures the address of the local variables. So "doUseFullThings" does not have access to those variables.

malloc statement returns pointer. This has not been handled properly.( probably you were trying to give us overview ). You must be taking care to free this somewhere.

Since this is a mixture of C and C++ code, it is difficult to use RAII (taking care of allocated memory), Perfect forwarding ( avoid copying variables), Lambda functions ( to access local varibales) etc. Under the circumstances, your approach seems to be the way to go.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top