1

I would like to make function to be callable (e.g. exposed) to the rest of the library through the macro only, to prevent accidental undesired side effects.

Why? This is because I have a variadic function, which could be called from another variadic function and that way I would like to add NULL sentinel to the call, using macro, thus making access to va_list much easier and prevent undefined behaviour. There are also other handy scenarios, where this could really be helplful.

An example :

test.h

void _func(char *dummy, ...);
//I would like to make sure that rest of the library
//only calls _func through this macro
#define func(dummy, ...) _func(dummy, __VA_ARGS__, NULL)

test.c

//Implementation of the _func function
static void _func(char *dummy, ...) {
     //body goes here...
}

main.c

int main(int argc, char *argv[]) {
    //This should not be allowed by compiler
    _func("dummy", "arg1");
    //This should be allowed by compiler, but since definition
    //of _func is static in test.c file, compiler is not happy anyway
    //LNK2001   unresolved external symbol __func
    func("dummy", "arg1");
    return 0;
}

I've already tried with #define and #undef compiler directives to somehow force this scenario, but no avail. Is this even possible in C?

  • Unfortunately you can't really do what you want, since the macro invocation `func(...)` would be replaced by `_func(...)`, and if `_func` is not available then it won't be available no matter what. The best solution (IMO) is to ***document*** the function to need the `NULL` terminating argument, and that not providing it will lead to *undefined behavior*. You already *have* some documentation to say that it will only take pointer arguments, right? Then just extend that documentation with the terminator requirement. – Some programmer dude Jul 27 '17 at 18:21
  • Your _func will not be visible from any other compilation units as it is declared `static`. Macro is not something you can "call". Your function definition in the .h file is different that that function itself so you will get the compiler error. Anyway your idea is just completely useless (probably because you dont know how the preprocessor works and what static means). As @Some programmer dude wrote - trust the potential users and just write the comment in the header file and in the documentation how to use the function. They will have to trust you to use your library. – 0___________ Jul 27 '17 at 18:53
  • Thank you all, I will still try to go with the answer(s) below...for now. Cheers! – i_hack_localhosts Jul 27 '17 at 19:53

3 Answers3

3

You can shadow the function with a macro:

void _func(char *dummy, ...);
#define _func(...) error_use_the_macro_func_instead_of_calling__func_directly

// Always use the macro "func" instead of calling "_func" directly.
#define func(dummy, ...) (_func)(dummy, __VA_ARGS__, NULL)

Notice the parentheses around _func in the macro. This prevents the _func from being recognized as a function-like macro and gives the macro access to the function. If somebody tries to call _func directly, they get

error C2065: 'error_use_the_macro_func_instead_of_calling__func_directly': undeclared identifier

This "macro shadowing" technique has the advantage of being usable in expression contexts:

for (int i = 0; i < 5; func("incrementing i", ++i)) { ... }

or if we change the situation slightly and give _func a return value:

int _func(char *dummy, ...);
#define _func(...) error_use_the_macro_func_instead_of_calling__func_directly

// Always use the macro "func" instead of calling "_func" directly.
#define func(dummy, ...) (_func)(dummy, __VA_ARGS__, NULL)

then this allows you to do things like

int i = func("hello", 2) * func("there", 3);
Raymond Chen
  • 44,448
  • 11
  • 96
  • 135
  • Thank you very much @Raymond Chen, will try this out! Cheers! – i_hack_localhosts Jul 28 '17 at 16:27
  • Unfortunately, this won't work as _ func macro is expanded and collides with implementation of the real _ func (which is in separate file e.g. test.c), receiving **error C2054: expected '(' to follow error_use_the_macro_func_instead_of_calling__func_directly'**.I'm using MSVC compiler 14.0 (building from within VS 2015). Any suggestions? – i_hack_localhosts Jul 28 '17 at 17:06
  • You have to use `#define _func(...) error_blah` with the parentheses in order for it to be defined as a function-like macro. If you take a shortcut and use `#define _func error_blah`, then it won't work. I copy-pasted my example into a new VS2015 C project and it correctly accepted `func("hello", 1);` and rejected `_func("hello", 1);`. – Raymond Chen Jul 28 '17 at 19:54
  • yes that's exactly what I did, used function-like macro. Please refer to this [gist](https://gist.github.com/Civa/b7750525cf4c3377a9900173a5d526ce). Thank you. – i_hack_localhosts Jul 28 '17 at 20:56
  • Look carefully at the error. it's complaining about `restrict.c`, not `main.c`. You need to `#undef _func` in `restrict.c` so you can define the function. – Raymond Chen Jul 28 '17 at 23:06
  • Yes! Figured iit out now, thank you very much for your time and effort. – i_hack_localhosts Jul 28 '17 at 23:09
0

Maybe you can scope the visibility of the private function? Here's a snippet to illustrate what I mean. Not pretty, but it may work for you(no MSVC to test with here)

#define func(a, b) do { \
    extern void private_func(int , int );\
    private_func(a, b);\
 } while (0)

void foo(void)
{
    func(1, 2);
    private_func(3, 4);
}
Bjorn A.
  • 1,148
  • 9
  • 12
  • And what it has common with the variable number of arguments? This is a trivial example with the fixed number of parameters. Try to make it variable with the ending NULL – 0___________ Jul 27 '17 at 19:07
  • and you read the rest. You example is completely useless as you can can make `private_func` `static` and only the wrapper function visible to the "world" without those funny macros. But the OP idea was to create macro which will add `NULL` after the variable number of parameters. So you be serious and read the question.To be more funny your example will not compile at all. – 0___________ Jul 27 '17 at 19:36
  • @PeterJ, it is ok.Bjorn gave me just enough information to work with. This kind of solves my problem which I will present shortly answering my own question. – i_hack_localhosts Jul 27 '17 at 19:44
  • @PeterJ The snippet was written to not compile, to show that the compiler would warn if one called the "private" function directly. If you want me to, I can add a NULL arg to the private_func, but OP got the point. Obviously, you didn't... – Bjorn A. Jul 27 '17 at 19:52
0

What @Bjorn A. has written in the post above, actually solves my problem as compiler gets angry with the message : '_func': redefinition; different basic types if I try to call _func directly.

Here is the adopted example :

test.h

#define func(dummy, ...) do { \
    extern void _func(char *, ...);\
    _func(dummy, __VA_ARGS__, NULL);\
 } while (0)

test.c

//Implementation of the _func function
//static has to be omitted here, but it doesn't matter
void _func(char *dummy, ...) {
     //body goes here...
}

main.c

int main(int argc, char *argv[]) {
    //'_func': redefinition; different basic types
    //if we try to call _func directly
    _func("dummy", "arg1");
    //this is ok
    func("dummy", "arg1");
    func("dummy2", "arg2");
    return 0;
}

EDIT : Actually, @Raymond Chen has proposed much better solution with function shadowing - idea is to enclose the function name with parentheses to stop preprocessor from expanding it. More info about that here.

Here is the final (hopefully) solution that works like a charm :

test.h

void _func(char *dummy, ...);
#define _func(...) error_use_the_macro_func_instead_of_calling__func_directly
#define func(dummy, ...) (_func)(dummy, __VA_ARGS__, NULL)

test.c

//Notice the _func is enclosed with parentheses here
void (_func)(char *dummy, ...) {
     //body goes here...
}

main.c

int main(int argc, char *argv[]) {
    //C2065 'error_use_the_macro_func_instead_of_calling__func_directly': undeclared identifier
    //if we try to call _func directly
    _func("dummy", "arg1");
    //this is ok
    func("dummy", "arg1");
    func("dummy2", "arg2");
    return 0;
}

Many thanks! Cheers!

  • You're welcome. :) Please note that the "different basic types" warning is because _func() returns void. If it returned int, the compiler may not have warned. – Bjorn A. Jul 27 '17 at 19:54