1

I'm using the XC8 compiler. For that, you have to define your own void putch(char data) function in order for functions like printf() to work, as is described here. Basically, putch() is the function which is used to write characters to stdout.

I now want to change this function on the fly. I have two different functions, putch_a() and putch_b() and want to be able to change which one is used for putch() itself, on the fly.

I thought of this:

unsigned use_a_not_b;
void putch(char data) {
   if (use_a_not_b) {
      putch_a(data);
   } else {
      putch_b(data);
   }
}

However, this reduces execution speed. Would there be a way to use pointers for this? I have read this answer, and made the following code:

void putch_a(char data);
void putch_b(char data);

void (*putch)(char) = putch_a; // to switch to putch_a
void (*putch)(char) = putch_b; // to switch to putch_b

Would that work? Is there a faster or better-practice way?

Community
  • 1
  • 1
  • It probably won't even compile. – Basile Starynkevitch May 05 '13 at 08:05
  • Does a single if...else and a call/return really reduce execution speed noticeably in an i/o function? Compared to all the time spent checking if buffer space is free, calculating buffer offsets, informing hardware data is available, being interrupted while data is actually transmitted to hardware, etc. Your first block should be fine. – The Photon May 05 '13 at 14:28
  • @ThePhoton you are right for this case, but what if one would want to choose more functions? That's how I thought up there should be another option to do this. –  May 05 '13 at 14:30
  • If putch_a and putch_b are inlineable this doesn't even cost an extra call/return. For more cases, you can have an integer `which_putch` and a case statement...which the compiler will optimize very well. If you want to get fancy @undefinedbehavior's solution also works, but *does* cost an extra call/return. – The Photon May 05 '13 at 14:34
  • @ThePhoton it of course depends on how much different functions you're going to use. For small amounts, a switch statement is probably the fastest indeed. For bigger amounts (I haven't tested how big), the answer will be faster. –  May 05 '13 at 14:40

3 Answers3

2

No, and why not

To answer your question: no you can't, in the way that you are thinking (i.e. function pointer). What a function pointer is is a variable with an address of another variable. To illustrate, consider how this works when you have a function pointer foo pointing to function bar.

int bar() {
}

void baz(int (*foo)()) {
   int x = foo();     // Calls the function pointed to bar foo
}

int main() {
   int (*foo)();
   foo = &bar;
   baz(foo);    // Cal baz() passing it foo, which points to bar()
}

What foo holds is the address of bar. When you pass foo to some function that expects a function pointer parameter (in this case baz()), the function dereferences the pointer, i.e. looks at the memory address associated with foo, gets the address sored in it, in our case the the address of bar, and then calls a function (in our case, bar) at that address. To be very careful about this: in the above example baz() says

  • Let me look at the memory associated with foo, it has another address in it
  • Load that address from memory, and call a function at that address. That function returns an int and takes no parameters.

Let's contrast this with a function that calls bar() directly:

void qux() {
   int x = bar();     // Call bar()
}

In this case there is no function pointer. What there is, is an address, supplied by the linker. The linker lays out all the functions in your program, and it knows, for instance that bar() is at address 0xDEADBEEF. So in qux() there is just a jump 0xDEADBEEF call. In contrast in baz() there is something like (pseudo-addembly):

pop bar off the stack into register A
read memory address pointed to by register A into register B
jump to memory location pointed to by register B

The way putch() gets called from printf(), for instance, is exactly like qux() calls bar(), and not like the way baz() does: putch gets statically linked into your program, so the address of putch() is hardcoded in there, simple because fprintf() doesn't take a function pointer to call for a parameter.

Why #define is not the answer

#define is a preprocessor directive, that is, "symbols" defined with #define are replaced with their values before the compiler even sees your code. This means that #define makes your program less dynamically modifiable not more. This is desirable in some cases, but in your case it will not help you. To illustrate if you define a symbol like this:

#define Pi 3.14

Then everywhere you use Pi it is as if you typed 3.14. Bacause Pi does not exist, as far as the compiler is concerned, you cannot even take an address of it to make a pointer to it.

Closest you can get to a dynamic putch

Like the others have said, you can have some sort of case statement, conditional, or a global pointer, but the putch function itself has to be there in the same form.

Global function pointer solution:

   void (*myPutch)(char);

   putch(char ch) {
       myPutch(ch);
   }

   int main() {
       myPutch = putch_Type_A();

       ...

       myPutch = putch_Type_B();

   }

If/then/else solution has been provided in other answers

goto solution: This would be an ugly (but fun!) hack, and only possible on von Neumann-type machines, but in these conditions you could have your putch look like this:

   putch(char ch) {
       goto PutchTypeB
   PutchTypeA:
       // Code goes here
       return; 
   PutchTypeB:
       // Code goes here
       return; 

   }

You would then overwrite the goto instruction with goto to some other memory address. You'd have to figure out the opcodes for doing this (from disassembly, probably), and this isn't possible on Harvard architecture machines, so it is out on AVR processors, but it would be fun, if cludgey.

angelatlarge
  • 4,086
  • 2
  • 19
  • 36
1

No. That isn't guaranteed to work due to the way code is generated and linked. However...

void (*output_function)(char) = putch_a;

void putch(char c) {
    output_function(c);
}

Now you can change output_function whenever you like...

There is no concept of "speed" in C. That's an attribute introduced by implementations. There are fast implementations (or rather, implementations that produce fast code, in the case of "compilers") and slow implementations (or implementations that produce slow code).

Either way, this is unlikely to be a significant bottleneck. Produce a program that solves a useful program, profile it to determine the most significant bottlenecks and work on optimising those.

autistic
  • 1
  • 3
  • 35
  • 80
  • Thanks! Could I also do a `goto` or `#define`? Wouldn't that be faster? –  May 05 '13 at 08:10
  • @CamilStaps See the edit to my answer... I don't see how `goto` or `#define` could work at all. The best chance you have of solving this problem is with the code I have produced. – autistic May 05 '13 at 08:16
1

Before you optimize this, make sure what you have really does reduce execution speed noticeably. In an i/o function there's usually a lot of other stuff going on (checking if buffer space is free, calculating buffer offsets, informing hardware data is available, being interrupted while data is actually transmitted to hardware, etc.) that would make a single extra if/else inconsequential.

In most cases your first block should be fine.

In comments you mention maybe needing to extend this structure to multiple putch() functions.

Maybe try

enum PUTCH { sel_putch_a, sel_putch_b, ... };

enum PUTCH putch_select;

void putch(char c) {
    switch(putch_select) {
        case sel_putch_a : putch_a(c); break;
        case sel_putch_b : putch_b(c); break;
        /* ... */
    }
}

The compiler should be able to optimize the switch statement to a simple computation and a goto. If the putch_<n> functions are inlineable, this doesn't even cost an extra call/return.

The solution using a pointer-to-function in another answer is more flexible in terms of being able to change the available putch functions on the fly, or define them in other files (for example, if you're writing a library or framework to be used by others), but it does require an extra call/return overhead (compared to the simple case of just defining a single putch function).

The Photon
  • 1,242
  • 1
  • 9
  • 12