1

I have a function to print debug logs which has to be toggled depending on the environment variable. Instead of checking the env var each time the print_trace() is called, what should be the best method to store it and reuse that value?

void print_trace(const char* msg)
{
    const char* s = getenv("DEBUG_TRACE");

    if(!strcmp(s,"ON"))
      printf(msg);
}

There is no main() as this is a shared library.

a11apurva
  • 138
  • 10
  • `'ON'`? Shouldn't it be `"ON"`? – Some programmer dude Sep 20 '21 at 14:42
  • Also, remember that `strcmp` returns `0` (i.e. *false*) if the strings are equal. – Some programmer dude Sep 20 '21 at 14:42
  • Yes you are right, thanks, updated it. – a11apurva Sep 20 '21 at 14:45
  • Lastly about your problem: Almost all dynamic libraries will have some kind of on-load function, which can be used to do some initialization. How this works depends on your operating system, of course. If you want a portable solution, add an "init" function which is used to initialize the library and must be called early in program execution. – Some programmer dude Sep 20 '21 at 14:46
  • 1
    `getenv()` may return `NULL` in case of errors. Add `if (s == NULL) { return; }` as a check before the `strcmp()`. – Luca Polito Sep 20 '21 at 14:47
  • You can create a function which is called once when your library is loaded by using [`__attribute__((constructor))`](https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html), or look [here](https://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc) for some pointers of how to do this in MSVC. Barmar's answer is the best way to do it cross-platform, although it might call `getenv()` multiple times if you have multiple threads calling `print_trace()` simultaneously. – G. Sliepen Sep 20 '21 at 14:49

2 Answers2

6

You could save the result of the decision in a static variable.

void print_trace(const char* msg)
{
    static int debug_on = -1; // -1 == not yet set
    if (debug_on == -1) {
        const char* s = getenv("DEBUG_TRACE");
        debug_on = s && (strcmp(s, "ON") == 0);
    }

    if(debug_on)
      printf("%s", msg);
}
Barmar
  • 741,623
  • 53
  • 500
  • 612
1

You could use the thread safe call_once feature that was added in C11.

Example:

#include <threads.h>

static bool debug_mode;                   // your debug mode flag

void set_debug_mode(void) {               // this is only called once
    const char *s = getenv("DEBUG_TRACE");
    debug_mode = s && !strcmp(s, "ON");
}

void print_trace(const char* msg) {
    static once_flag flag = ONCE_FLAG_INIT;
    call_once(&flag, set_debug_mode);     // called once to set debug_mode

    if(debug_mode)
        printf(msg);
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • It should be noted that is a synchronization feature. `call_once` guarantees synchronization between multiple threads, so it has some costs of locking or other synchronization. Its purpose is to ensure that, when multiple threads might attempt something at the same time, only one occurs. That is overkill for OP’s request, where there is no stated need for synchronization, and a simple static flag suffices. – Eric Postpischil Sep 20 '21 at 15:04
  • @EricPostpischil It may be overkill, not sure. I'll leave it here as an alternative that may be worth looking into. – Ted Lyngmo Sep 20 '21 at 15:12
  • @EricPostpischil If it's only used once in the entire program run, the locking overhead should be negligible. – Barmar Sep 20 '21 at 18:42
  • @Barmar I'm not sure how it's implemented but I expect an atomic bool to be checked for every call to `print_trace`. I think locking will only have to be done if the atomic bool is not set (that is, on the first call). [This](https://godbolt.org/z/4YovEoT3z) is how I imagine it. Not sure if there's anything else needed though. – Ted Lyngmo Sep 20 '21 at 19:07