41

I was asked a very interesting question during a C interview: How can you implement a function f() in such a way that it can only be called from a particular g() function. If a function other than g() tries to call f() it would result in a compiler error.

At first, I though this could be done with function pointers and I could get close to blocking the call at runtime. But I was not able to think of a compile time strategy. I don't even know if this is possible using ansi C.

Does anyone have any idea?

Walt W
  • 3,261
  • 3
  • 30
  • 37
luizleroy
  • 508
  • 1
  • 4
  • 6
  • Are you sure the question was exactly like this? It does not make any sense to me. Perhaps if it would be about member functions, then it perhaps might have some merit, but ordinary functions? – Suma Sep 09 '09 at 20:20
  • @Suma: There wouldn't be member functions in C. – Walt W Sep 09 '09 at 20:26

7 Answers7

47

Here's one way:

int f_real_name(void)
{
    ...
}

#define f f_real_name
int g(void)
{
    // call f()
}
#undef f

// calling f() now won't work

Another way, if you can guarantee that f() and g() are the only functions in the file, is to declare f() as static.

EDIT: Another macro trick to cause compiler errors:

static int f(void) // static works for other files
{
    ...
}

int g(void)
{
    // call f()
}
#define f call function

// f() certainly produces compiler errors here
Chris Lutz
  • 73,191
  • 16
  • 130
  • 183
  • 3
    However, you can still call f_real_name(). – David Thornley Sep 09 '09 at 20:08
  • 1
    This is true, but not strictly a requirement. Play to the rules, not the intentions. – Chris Lutz Sep 09 '09 at 20:09
  • 3
    Of course we can call `f_real_name()`. We can also call `printf()`, `malloc()` and many other nice functions, but what has it to do with calling `f()`? – P Shved Sep 09 '09 at 20:14
  • 2
    Anyone care to explain the downvote? I know it's not what the OP is looking for, but it _does_ satisfy his criteria. – Chris Lutz Sep 09 '09 at 20:16
  • 1
    I think it is EXACTLY what the OP is looking for. And Chris mentions the static method. C'mon people. – Walt W Sep 09 '09 at 20:18
  • Updated with a new trick. I think it's more fun than the old one. – Chris Lutz Sep 09 '09 at 20:19
  • Also, you could use function pointers to achieve the same trick (have `f` be a function pointer defined in `g()` to `f_real_name()` so that only `g()` can call `f()` but other functions can't) but it's essentially the same trick, and no safer or more reliable than the macros. – Chris Lutz Sep 09 '09 at 20:28
31

Put g() and f() in the same module, and declare f() as static. The static keyword makes f() available only to functions in the same module, or source file.

You might also want to mention that no other methods should be allowed in the module with f() and g(), otherwise they could call f().

PS - I really think Chris Lutz' answer is actually the best. It mentions this approach, but also a clever macro renaming that works with fewer environmental conditions (does not require the module file specifically for these two functions).

Note also that with a macro, you could do the following:

#define f() f_should_not_be_called_by_anything_except_g

Which would present a nice error message, and auto-completers (like Visual Studio) would show that tip when the user types f().

Walt W
  • 3,261
  • 3
  • 30
  • 37
  • 1
    Check the comment I made @John Milikin. – luizleroy Sep 09 '09 at 20:11
  • I'm aware. Pretty easy to fix though, really. – Walt W Sep 09 '09 at 20:12
  • Actually, I'm a fan of Jonathan Leffler's answer, but as he said it's not generalizable. But I have upvoted nearly everyone who said the correct (i.e. `static` function) answer. Probably what they were looking for. – Chris Lutz Sep 09 '09 at 20:21
  • I'm not a fan of that answer, because function prototypes are a clean way around that. The function ordering is important to C compilers, but not as important as the module they are in. – Walt W Sep 09 '09 at 20:26
  • @Walt - That's a nice one. I hadn't thought of that. GCC is giving me errors when I try that kind of trick under -Wall -Wextra, but I can imagine it working... – Chris Lutz Sep 09 '09 at 20:31
  • @Chris: Yeah, I tried it under VS; you mean you're getting an error in the #define as opposed to when f() is called, I presume. – Walt W Sep 09 '09 at 20:35
  • @Walt - I was referring to your function-pointer trick to get around Jonathan's answer. Sorry. – Chris Lutz Sep 09 '09 at 21:42
  • It's not a function pointer trick . . . it's a function declaration trick. – Walt W Sep 09 '09 at 22:23
  • What about in the header file `#define f f_should_not_be_called_by_anything_except_g`, and then have `static type f(args) {/*do something*/}` in a file, and `#undef f`, then `type g{/*do_stuff*/f(args);/*do more stuff*/}`, then `#define f f_should_not_be_called_by_anything_except_g`. A programmer would have a tough time getting around that(unless he had access to the file with f and g and modified THAT). – JustinCB Sep 29 '17 at 14:28
18

You can make module-private functions with the static keyword:

static void func(void)
{
    // ...
}

Then, func() can only be called by other functions defined in the same file (technically, the same translation unit: other functions whose definitions are included by a #include directive can still access it). func is said to have internal linkage. All other functions (that is, without the static keyword) are said to have external linkage.

Beyond that, no, there is no way to make functions inaccessible. You can use macros to change the name of the function, but other code can always still access it with the appropriate name.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
13

Place f() and g() in the same source file, declare f() static.

John Millikin
  • 197,344
  • 39
  • 212
  • 226
11

An option for GCC is to use nested functions. While it's not standard C, it works quite well.

outis
  • 75,655
  • 22
  • 151
  • 221
3

It is only possible coincidentally.

If functions f() and g() are both in the same source file, and there are no other functions in the file, and if g() never returns the function pointer to f() to any of its callers, then making f() static will do the job.

If other functions must appear in the same source file, placing f() at the bottom of the file as a static function, and only defining g() immediately after it would achieve more or less the same effect - though if you didn't tell the compiler to generate errors on 'missing declarations' other functions could call it with warnings.

#include <stdio.h>

extern void g(void);    /* in a header */

/* Other functions that may not call f() go here */

static void f(void)
{
    puts("X");
}

void g(void)
{
    f();
}

Clearly, this technique cannot be extended reliably to another pair of functions in the same file - x() and y() - such that x() and only x() can call y() while g() and only g() can call f() at the same time.

However, normally you would rely on the programmers' discipline and simply make f() static in the source file, along with a comment that only g() may call it, and then discipline anyone who modifies the code so that a function other than g() calls f().

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

You could implement f() as a macro and let the pre-compiler handle it:

#include <stdio.h>

#define f(s) printf("Hello %s!\n", s);

void g(char * w) {
        f(w);
}
#undef f

int main(void) {
        /* f() calls will produce an undefined reference to 'f' linker error */
        g("World");
        return(0);
}

In this way, there is no other exposed function and no need for a static function.

bomba
  • 11
  • 2
  • So, "make `f()` not be a function after all"? Lateral thinking points for that, I guess. – John Bollinger Oct 04 '22 at 17:39
  • Matching the specs and reducing surface, if you ask me! Can you really tell that f() is not a function, from a compiler perspective? – bomba Oct 04 '22 at 17:46
  • Well, the specs do start with "implement **a function** f()". – John Bollinger Oct 04 '22 at 17:51
  • I think that the f() function is implemented, but as a macro. What is the difference from the compiler and linker perspective, after pre-compilation? You will have that symbol available, in the required scope. – bomba Oct 04 '22 at 19:21
  • I'm sorry, but "the [...] function is implemented [...] as a macro" is nonsensical. Functions are not macros, and macros are not functions. Again, I'll grant you lateral-thinking points, but this offering *does not* conform to the spec. And there indeed are differences, such as that the address of a function can be computed, but not that of a macro; that the arguments to a function are evaluated, exactly once each, before function execution begins, but that is not necessarily true of a macro; that function arguments are passed by value. – John Bollinger Oct 04 '22 at 19:44
  • I do agree on the differences that you have highlighted, and you should absolutely take those into account - if you need to. I am sorry but I disagree on this being not conforming the specs, in the given use case description. – bomba Oct 04 '22 at 20:10