开发者

Designing Around a Large Number of Discrete Functions in C

Greetings and salutations,

I am looking for information regrading design patterns for working with a large number of functions in C99.

Background:

I am working on a complete G-Code interpr开发者_JAVA百科eter for my pet project, a desktop CNC mill. Currently, commands are sent over a serial interface to an AVR microcontroller. These commands are then parsed and executed to make the milling head move. a typical example of a line might look like

N01 F5.0 G90 M48 G1 X1 Y2 Z3

where G90, M48, and G1 are "action" codes and F5.0, X1, Y2, Z3 are parameters (N01 is the optional line number and is ignored). Currently the parsing is coming along swimmingly, but now it is time to make the machine actually move.

For each of the G and M codes, a specific action needs to be taken. This ranges from controlled motion to coolant activation/deactivation, to performing canned cycles. To this end, my current design features a function that uses a switch to select the proper function and return a pointer to that function which can then be used to call the individual code's function at the proper time.

Questions:

1) Is there a better way to resolve an arbitrary code to its respective function than a switch statement? Note that this is being implemented on a microcontroller and memory is EXTREMELY tight (2K total). I have considered a lookup table but, unfortunately, the code distribution is sparse leading to a lot of wasted space. There are ~100 distinct codes and sub-codes.

2) How does one go about function pointers in C when the names (and possibly signatures) may change? If the function signatures are different, is this even possible?

3) Assuming the functions have the same signature (which is where I am leaning), is there a way to typedef a generic type of that signature to be passed around and called from?

My apologies for the scattered questioning. Thank you in advance for your assistance.


1) Perfect hashing may be used to map the keywords to token numbers (opcodes) , which can be used to index a table of function pointers. The number of required arguments can also be put in this table.

2) You don's want overloaded / heterogeneous functions. Optional arguments might be possible.

3) your only choice is to use varargs, IMHO


I'm not an expert on embedded systems, but I have experience with VLSI. So sorry if I'm stating the obvious.

The function-pointer approach is probably the best way. But you'll need to either:

  1. Arrange all your action codes to be consecutive in address.
  2. Implement an action code decoder similar to an opcode decoder in a normal processor.

The first option is probably the better way (simple and small memory footprint). But if you can't control your action codes, you'll need to implement a decoder via another lookup table.

I'm not entirely sure on what you mean by "function signature". Function pointers should just be a number - which the compiler resolves.

EDIT: Either way, I think two lookup tables (1 for function pointers, and one for decoder) is still going to be much smaller than a large switch statement. For varying parameters, use "dummy" parameters to make them all consistent. I'm not sure what the consequences of force casting everything to void-pointers to structs will be on an embedded processor.

EDIT 2: Actually, a decoder can't be implementated with just a lookup table if the opcode space is too large. My mistake there. So 1 is really the only viable option.


Is there a better way ... than a switch statement?

Make a list of all valid action codes (a constant in program memory, so it doesn't use any of your scarce RAM), and sequentially compare each one with the received code. Perhaps reserve index "0" to mean "unknown action code".

For example:

// Warning: untested code.
typedef int (*ActionFunctionPointer)( int, int, char * );
struct parse_item{
    const char action_letter;
    const int action_number; // you might be able to get away with a single byte here, if none of your actions are above 255.
    // alas, http://reprap.org/wiki/G-code mentions a "M501" code.
    const ActionFunctionPointer action_function_pointer;
};
int m0_handler( int speed, int extrude_rate, char * message ){ // M0: Stop
    speed_x = 0; speed_y = 0; speed_z = 0; speed_e = 0;
}
int g4_handler ( int dwell_time, int extrude_rate, char * message ){ // G4: Dwell
    delay(dwell_time);
}
const struct parse_item parse_table[] = {
    { '\0', 0, unrecognized_action }  // special error-handler 
    { 'M', 0, m0_handler }, // M0: Stop
    // ...
    { 'G', 4, g4_handler }, // G4: Dwell
    { '\0', 0, unrecognized_action }  // special error-handler 
}
ActionFunctionPointer get_action_function_pointer( char * buffer ){
    char letter = get_letter( buffer );
    int action_number = get_number( buffer );
    int index = 0;
    ActionFunctionPointer f = 0;
    do{
        index++;
        if( (letter == parse_table[index].action_letter ) and
            (action_number == parse_table[index].action_number) ){
            f = parse_table[index].action_function_pointer;
        };
        if('\0' == parse_table[index].action_letter ){
            index = 0;
            f = unrecognized_action;
        };
    }while(0 == f);
    return f;
}

How does one go about function pointers in C when the names (and possibly signatures) may change? If the function signatures are different, is this even possible?

It's possible to create a function pointer in C that (at different times) points to functions with more or less parameters (different signatures) using varargs.

Alternatively, you can force all the functions that might possibly be pointed to by that function pointer to all have exactly the same parameters and return value (the same signature) by adding "dummy" parameters to the functions that require fewer parameters than the others.

In my experience, the "dummy parameters" approach seems to be easier to understand and use less memory than the varargs approach.

Is there a way to typedef a generic type of that signature to be passed around and called from?

Yes. Pretty much all the code I've ever seen that uses function pointers also creates a typedef to refer to that particular type of function. (Except, of course, for Obfuscated contest entries).

See the above example and Wikibooks: C programming: pointers to functions for details.

p.s.: Is there some reason you are re-inventing the wheel? Could maybe perhaps one of the following pre-existing G-code interpreters for the AVR work for you, perhaps with a little tweaking? FiveD, Sprinter, Marlin, Teacup Firmware, sjfw, Makerbot, or Grbl? (See http://reprap.org/wiki/Comparison_of_RepRap_Firmwares ).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜