This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Pointer to function problems

Hi,

This post is about the classic problem creating pointers to function using the overlay procedure.

The reference in the manual suggest the OVERLAY directive to modify the call tree that is automatically generated by the linker. But what happeneds if we don't know the name of the indirect function the pointer calls? Yes this is true.

Consider the possibility of someone who try to build a lib. And this lib provide a function(funA) that calls an afterwards user defined procedure unknown by the lib function(funA). Here an example

//lib_file.h
// ...
typedef void (* Tpfun)(void);
void module_init (Tpfun pf);
void module (void);
// ...

//lib_file.c
static Tpfun    pfun;
//--------------------------------
void module_init (Tpfun pf)
{
        pfun = pf;
}
//----------------------
void module (void)
{
        //code
        // ...
        pfun();
        // code
}

In this simple program there is no call to module_init(&..) and it is impossible to know the address/name of the indirect function and so it is also impossible to use the OVERLAY directive(i think). We know only the pointer to function "pfun". We also don't know if the indirect function call other functions.
The only way to know the name of the indirect function is when the lib user decide to use the module and initializes the module using the module_init(&..);

But how can we build the lib so the user uses it???

The question is:

1)Is there a way to modify the call tree that is automatically generated by the linker in order to include the indirect function?
And if there is no way.
2)How can someone enable the normal way of function call using the stack?

I'm using the eval version C51 8.08

  • Do you really have to use a pointer?

    Can't you just insist that the library user must provide their function(s) with a specific name - rather like the 'C' language insists that the program entry point must be called main() ?

    //lib_file.h
    // ...
    void fun( void );          // The user must provide this function
    
    void module_init( void );  // Pointer no longer needed
    void module( void );
    

    //lib_file.c
    //--------------------------------
    void module_init( void )
    { // Pointer initialisation no longer required
    } //----------------------
    void module (void)
    { //code // ... fun(); // Just call the function by name // code
    }

  • The end user that supplies the callback is the one that builds the complete program, and thus the one that needs to fix up the call tree.

    See the reentrant keyword for declaration functions that push params and locals onto a traditional stack. You'll find that the code is likely bigger and slower, as it has to manipulate pointers in software. The 8051 architecture just doesn't have good support for pointers.

  • But what happeneds if we don't know the name of the indirect function the pointer calls? Yes this is true.

    Then you've just stuck yourself between a rock and a very hard place --- and for no particularly good reason, either. Think really hard if you have to go there.

    Not all techniques that work well for keeping big software systems under control are equally applicable for small microcontrollers, particularly the 8051. Passing around pointers to callback functions is definitely one of those. It seriously gets in the way.

  • Thanks to all for the prompt answer.


    Then you've just stuck yourself between a rock and a very hard place --- and for no particularly good reason, either. Think really hard if you have to go there.

    I have to say that the way Andy Neil suggests is the way I'm already doing it. But for someone who try to translate code from a compiler to another(like me) is a very difficult procedure. I'm working in education and i find the keil debugger user/student friendly, but still the translation of a library and the whole documentation is a nightmare.


    The end user that supplies the callback is the one that builds the complete program, and thus the one that needs to fix up the call tree.

    This is true, but i believe that a library must be complete for the end user. How can the user modify the call tree if he doesn't know how the library works. He is just the user.

  • This is true, but i believe that a library must be complete for the end user. How can the user modify the call tree if he doesn't know how the library works. He is just the user.

    If the library is written for '51 purposes (no function pointers) the call tree is not the end users job but the linkers.

    Erik

  • But the library is complete!

    This "callback" function (or whatever it is) must always be supplied by the end user.

    The only thing in question is: how should the library gain access to this user-provided function?
    The 2 choices are:
    1. By name;
    2. By a pointer.

    Since the pointer is a Really Bad Idea for an 8051, that really only leaves 1 option...

  • The only thing in question is: how should the library gain access to this user-provided function?
    The 2 choices are:
    1. By name;
    2. By a pointer.

    Since the pointer is a Really Bad Idea for an 8051, that really only leaves 1 option...

    If the library is written for '51 purposes (no function pointers but calleable by name) the call tree is not the end users job but the linkers.

    Andy, we DO agree. even if "you say tomahtos and I say tomatoes"

    Erik

  • ... but i believe that a library must be complete for the end user. How can the user modify the call tree if he doesn't know how the library works. He is just the user.

    When i say "complete" i mean that if you are the end user "you don't need to add or modify anything". Not even the call tree. It's a bad idea to ask for the user to know how your library works. And even if you know how to trick the call tree, probably one year later you will not.
    That's why i choose to use the name of the calleable function and not a pointer to it.

    But what is so difficult about pointer to functions and x51. Is it that hard for the linker? In contrast with KEIL i see it like a flaw...

  • But what is so difficult about pointer to functions and x51. Is it that hard for the linker? In contrast with KEIL i see it like a flaw...

    It has NOTHING to do with Keil (well in a way it does - if they had chosen a horrible implementation, there would be no call tree)

    It has EVERYTHING to do with the '51 architecture. The '51 is extremely limited re stack space (and fetches via the stack pointer are very processor heavy), and if the call tree had not been implemented you would have a bunch of stack overflows and a very sluggish performance.

    Thus, because of the ARCHITECTURE of the '51 you "In contrast with KEIL i see it like a flaw" that Keil did the most effieient inplementation possible.

    So, as often said before if you do not want to be concerned with processor architecture, stay away from the '51

    Erik

  • When i say "complete" i mean that if you are the end user "you don't need to add or modify anything". Not even the call tree.

    I'm quite sure we all understand your wish. It's just that we know better than to expect such wishes to come true.

    As I said before, not all those nice design patterns lend themselves well to the way a 8051 works. Arbitrary use of function pointers is not a good idea on an 8051.

    In my experience libraries aren't used all that much in C51. You've just come across one of the reasons. Another is that 8051 programs are usually small enough that pre-compiled libraries don't offer significant advantage over re-compilation.

  • In my experience libraries aren't used all that much in C51
    I use them a lot HOWEVER,
    In my experience libraries for which you do not have access to the source aren't used all that much in C51.

    Erik

  • "Is it that hard for the linker?"

    If you need to ask that question, then you haven't understood the problem!

    It is not just "hard" - it is impossible!

    The reason the Linker needs to know the call tree is so that it can know what data can be overlaid.

    The Linker needs to do this at the time the project is built - therefore it cannot possibly know what functions might end-up getting called via a pointer assigned at run time!

    "with KEIL i see it like a flaw..."

    The problem is that the 8051 architecture is inherently unsuited to putting parameters & automatic variables on the stack - which is what "normal" compilers do.
    Keil's "workaround" for this is to put parameters & automatic variables into fixed memory locations. Of course, this has some drawbacks - which is why "normal" compilers don't do it when they can use the stack!

    Overlaying then recovers some of this fixed memory usage, by allowing mutually-exclusive functions to share memory locations - effectively, this does at link time what a real stack does at runtime.

    See: http://www.keil.com/appnotes/docs/apnt_129.asp

  • effectively, this does at link time what a real stack does at runtime.

    Which means that recursive function call chains - or unknown call chains (pointers) - are not suited for a '51 processor.

    I don't know about any other architecture where the linker knows about the internals of all functions. A linker normally have to fixup addresses depending on order and size of individual modules.

    What the Keil linker does is a hack. But it is the only known method to make the '51 processor at least reasonably compatible with any high och medium level language. Keil is basically continuing something that was started with the PLM51 language from Intel.

  • "What the Keil linker does is a hack. But it is the only known method to make the '51 processor at least reasonably compatible with any high or medium level language."

    Exactly!

    It brings its problems - like all this call-tree analysis stuff - but can the OP suggest a better way?

  • I wouldn't call it a hack, so much as a fairly powerful optimization. Most C compilers generate code that spends a lot of run time pushing and popping onto a stack, and uses indirect references to access commonly used variables. The call tree analysis allows you to eliminate all that code and directly reference locals, temps, and parameters. Sometimes it would be nice to have this option on other architectures, even those that are much better suited to maintaining a stack and indirect references. For a great many programs, you don't actually need the features that all that run-time stack manipulation provides.

    The cost of eliminating the stack, however, is fairly severe -- no recursion, no reentrancy, and limitations on the use of function pointers.

    If you want, though, you can just declare all your functions reentrant and get the behavior you'd expect of a "normal" C compiler without the "hack". I don't think you'll like the results on the 8051 so much.