1

I'm currently using a simple preprocessor switch to disable/enable my debug statements. I have printf retargeted to the UART output, and then define my print function in a globally included header (globals.h) to make it easy to disable all debugging like this:

#ifdef USE_UART
   #define MY_PRINT(...) printf(__VA_ARGS__)
#else
   #define MY_PRINT(...) 
#endif

All my application files can then print debug messages over UART like this:

MY_PRINT("\t<<Battery Voltage: %d>>\r\n", vBat);

What I'm trying to do is have this be switched by external input (ie a button press) during run time. For example something like this:

void my_print(const char * pString){
    if (uart_mode == UART_ON){
        printf(pString);
    }
}

Where uart_mode could be toggled via the external input. I'm having trouble figuring out how to pass variable arguements properly into printf through this function. Is this possible? Is there a better way to do it?

Otus
  • 305
  • 1
  • 5
  • 16
  • 5
    This is what "vprintf" is for. – David Schwartz Dec 01 '15 at 20:24
  • Can you show us the exact macros you are using? I was going to craft replacement macros for you, but I need to thoroughly understand the exact semantics of your existing macros. For example, as your macros are now, `if (x) MY_PRINT(bar); else baz();` is illegal (dangling else). So I would make macros that keep that. (If you don't care, then say so. But it's hard to craft a replacement for "something like this".) – David Schwartz Dec 01 '15 at 20:26
  • My 'something like this' I actually meant 'like this' :) Description is updated – Otus Dec 01 '15 at 20:34
  • Are you actually using `MY_PRINT` or `D_PRINT` ? – nnn Dec 01 '15 at 20:36
  • Sorry, that was a typo. MY_PRINT – Otus Dec 01 '15 at 20:42

3 Answers3

4

A decent approach that avoids conditionals at runtime (though it's still dynamic dispatch) and mimics function calls precisely (because it is a function call) would be:

typedef int (*printf_func_t)(const char *, ...);

int dummy_printf(const char *format, ...) {
    return 0;
}

/* Set initial value based on initial uart_mode */
printf_func_t dynamic_printf = uart_mode == UART_ON ? &printf : &dummy_printf;

In some other toggle code (where uart_mode is changed), you'd just test and reassign:

dynamic_printf = uart_mode == UART_ON ? &printf : &dummy_printf;

Actual users then always call dynamic_printf in place of printf, and it calls whichever function is currently assigned to the function pointer. No read or test of uart_mode occurs when printing, it just calls whatever function is found at the time.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Note: If the use of `dynamic_printf` is threaded (so one thread might change it while another is calls it), you'd probably want to declare it a `volatile` pointer to prevent caching of the function pointer into a register (and on some architectures, to provide read/write atomicity, so the function pointer is never seen partially rewritten). – ShadowRanger Dec 01 '15 at 20:58
  • Nice. Is the `&` needed with `&printf`? – chux - Reinstate Monica Dec 01 '15 at 20:58
  • 1
    @chux: [No](https://stackoverflow.com/questions/16917043/do-function-pointers-need-an-ampersand), I'm just being explicitly "pointer correct" out of habit (same way I tend to use prefix increment/decrement out of habit so I don't use postfix in C++ with a user defined class by accident). – ShadowRanger Dec 01 '15 at 21:30
  • This solution is inefficient: the arguments to `dynamic_printf` are always evaluated and pushed even if no debug output is required. Much better to use a macro with a test and skip the `printf`, especially since branch prediction will minimize the cost to almost 0. – chqrlie Dec 01 '15 at 22:14
  • @chqrlie: That is a good point. That said, failing to evaluate the arguments carries its own risks. Function calls with side-effects that only execute when you do a "real" print are a source of incredibly frustrating bugs; it's often considered a good idea to evaluate the arguments unconditionally, so you're not stuck with mysterious differences in behavior based on whether or not you're logging. After all, for the stuff that isn't function calls, the cost to set up the stack frame, call the function, then tear down the stack frame isn't that high either (especially in C; no exceptions/RAII). – ShadowRanger Dec 01 '15 at 23:12
  • The macro should definitely be spelled in uppercase to stress that it is debugging information that may or may not be compiled, therefore side effects should not be expected to always occur. Trying to produce readable output for debugging may incur non trivial overhead in the form of function calls, symbol lookups, number conversions... – chqrlie Dec 01 '15 at 23:40
3

How about something like this:

#define MY_PRINT(...)                       \
    do {                                    \
         if (uart_mode == UART_ON)          \
             printf(__VA_ARGS__);           \
    } while(0)
David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • 4
    Might want to use `while(0)` instead. `false` may or may not be defined unless `stdbool.h` is imported. – Mr. Llama Dec 01 '15 at 20:29
  • 1
    prefer `while(0)` over `while(false)`. Do not assume `` was included. – chqrlie Dec 01 '15 at 20:29
  • 1
    Note: In some situations, the return value of `printf()` is used. This answer prevents that rarely used functionality. – chux - Reinstate Monica Dec 01 '15 at 20:41
  • @chux: what should be the return value if the test fails? – chqrlie Dec 01 '15 at 22:09
  • @chqrlie If there is no sensible return value, having the function as `void f()` is best. Else OP would need to determine if a constant liek `0` or a call to a dummy `printf()` is better as in http://stackoverflow.com/a/34029868/2410359 – chux - Reinstate Monica Dec 01 '15 at 22:24
  • @chux: In fairness, returning a constant like 0 isn't unreasonable. `printf`'s return is the number of bytes written; when it's a dummy, no bytes were written. – ShadowRanger Dec 01 '15 at 23:14
2

As David Schwartz said, that's what vprintf is for.

#include <stdarg.h>
void my_print(const char * pString, ...){
    va_list args;
    va_start(args, pString);
    if (uart_mode == UART_ON){
        vprintf(pString, args);
    }
    va_end(args);
}
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
nategoose
  • 12,054
  • 27
  • 42
  • 1
    Though apparently not required by OP, could use `int my_print(...` and return the result of `vprintf()` to match `int printf(...` – chux - Reinstate Monica Dec 01 '15 at 20:53
  • Thanks this is what I ended up doing and it's working nicely. – Otus Dec 01 '15 at 21:00
  • This solution is inefficient: the arguments to `my_print` are always evaluated and pushed even if no debug output is required. Much better to use a macro with a test and skip the `printf`, especially since branch prediction will minimize the cost to almost 0. – chqrlie Dec 01 '15 at 22:15
  • @chqrlie If the code can run correctly in debug mode with the extra debug output, then the cost savings is not needed. In general, I agree, it would be better to reduce useless CPU usage> Yet surprising more efficient code itself introduces new timing in communication that may cause issues. – chux - Reinstate Monica Dec 01 '15 at 22:29
  • What the OP is implementing is dynamically selectable debug mode, just like debugging `malloc` with environment variables. The code is likely always compiled, evaluating the debug arguments and calling a dummy function is wasteful if output is disabled. – chqrlie Dec 01 '15 at 22:33
  • It's not about performance, it's about semantics. The original functions didn't evaluate the arguments when debugging wasn't enabled. Evaluating them in that case is a semantic change that may be undesirable, desirable, or even catastrophic. Only the OP can say. – David Schwartz Dec 01 '15 at 22:36
  • Power consumption is actually a concern so perhaps I'll look into using a macro instead.... – Otus Dec 02 '15 at 15:00
  • The OP included a "something like this" function in the question which is why they encountered an issue that they needed help with. The idea was to have an external signal witch on the UART output. Using a macro would increase the code image, but I don't know if that is an issue here. Power usage wasn't mentioned as a concern, but evaluating arguments and pushing them to the stack shouldn't cost much. The code that I wrote was optimized for readability and predictable behavior. – nategoose Dec 04 '15 at 19:18