views:

1015

answers:

6

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?

+13  A: 

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
Check the comment I made @John Milikin.
luizleroy
I'm aware. Pretty easy to fix though, really.
Walt W
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
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
@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
@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
@Walt - I was referring to your function-pointer trick to get around Jonathan's answer. Sorry.
Chris Lutz
It's not a function pointer trick . . . it's a function declaration trick.
Walt W
Ah. What am I reading?
Chris Lutz
+2  A: 

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

John Millikin
I don't thinks that's the expected answer. This doesn't stop anyone from putting other functions in the same source and cal f().
luizleroy
Anyone who can edit you source function can overcome *any* scheme for making something private.
dmckee
+9  A: 

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
However, you can still call f_real_name().
David Thornley
This is true, but not strictly a requirement. Play to the rules, not the intentions.
Chris Lutz
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()`?
Pavel Shved
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
I think it is EXACTLY what the OP is looking for. And Chris mentions the static method. C'mon people.
Walt W
Updated with a new trick. I think it's more fun than the old one.
Chris Lutz
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
+4  A: 

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
+1  A: 

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
+5  A: 

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

outis