【问题标题】:Function visibility in C : make function visible and callable through macro only (MSVC compiler)C 中的函数可见性:仅通过宏使函数可见和可调用(MSVC 编译器)
【发布时间】:2017-07-27 18:17:20
【问题描述】:

我想让函数只能通过宏调用(例如公开)到库的其余部分,以防止意外的不良副作用。

为什么?这是因为我有一个可变参数函数,可以从另一个可变参数函数调用它,这样我想使用宏将NULL sentinel 添加到调用中,从而更容易访问 va_list 并防止未定义的行为。还有其他方便的场景,这真的很有帮助。

一个例子:

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...
}

ma​​in.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;
}

我已经尝试使用#define#undef 编译器指令以某种方式强制这种情况,但无济于事。这在C语言中甚至可能吗?

【问题讨论】:

  • 不幸的是,你不能真正做你想做的事,因为宏调用func(...) 将被_func(...) 替换,如果_func 不可用,那么它将不可用 no有什么关系。最佳解决方案 (IMO) 是记录需要NULL 终止参数的函数,不提供它会导致未定义的行为。你已经一些文档说它只会接受指针参数,对吧?然后只需使用终止符要求扩展该文档即可。
  • 您的 _func 将不会在任何其他编译单元中可见,因为它被声明为 static。宏不是您可以“调用”的东西。您在 .h 文件中的函数定义与该函数本身不同,因此您将收到编译器错误。无论如何,您的想法完全没用(可能是因为您不知道预处理器是如何工作的以及静态的含义)。正如@Some程序员花花公子所写的那样-相信潜在用户,只需在头文件和文档中写下如何使用该功能的注释。他们必须信任您才能使用您的图书馆。
  • 谢谢大家,我仍然会尝试使用下面的答案......现在。干杯!

标签: c macros


【解决方案1】:

您可以使用宏来隐藏函数:

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)

注意宏中_func 周围的括号。这可以防止_func 被识别为类似函数的宏,并允许宏访问该函数。如果有人尝试直接拨打_func,他们会得到

错误 C2065:'error_use_the_macro_func_instead_of_calling__func_directly':未声明的标识符

这种“宏阴影”技术的优点是可以在表达式上下文中使用:

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

或者如果我们稍微改变一下情况,给_func一个返回值:

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)

那么这可以让你做类似的事情

int i = func("hello", 2) * func("there", 3);

【讨论】:

  • 非常感谢@Raymond Chen,会试试这个!干杯!
  • 不幸的是,这不起作用,因为 _ func 宏被扩展并与真正的 _ func 的实现发生冲突(在单独的文件中,例如 test.c),收到 错误 C2054:预期'(' 跟随 error_use_the_macro_func_instead_of_calling__func_directly'。我正在使用 MSVC 编译器 14.0(从 VS 2015 内部构建)。有什么建议吗?
  • 您必须使用带括号的#define _func(...) error_blah 才能将其定义为类似函数的宏。如果您走捷径并使用#define _func error_blah,那么它将无法正常工作。我将我的示例复制粘贴到一个新的 VS2015 C 项目中,它正确接受 func("hello", 1); 并拒绝 _func("hello", 1);
  • 是的,这正是我所做的,使用了类似函数的宏。请参考此gist。谢谢。
  • 仔细查看错误。它在抱怨restrict.c,而不是main.c。您需要在restrict.c 中添加#undef _func,以便定义函数。
【解决方案2】:

也许您可以限定私有函数的可见性?这是一个 sn-p 来说明我的意思。不漂亮,但它可能对你有用(这里没有 MSVC 可以测试)

#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);
}

【讨论】:

  • 它与可变数量的参数有什么共同点?这是一个具有固定数量参数的简单示例。尝试使用结尾 NULL 使其可变
  • 然后您阅读其余部分。您的示例完全没用,因为您可以使 private_func static 并且只有包装函数对“世界”可见,而没有那些有趣的宏。但是 OP 的想法是创建宏,它将在可变数量的参数之后添加 NULL。所以你要认真阅读这个问题。更有趣的是,你的例子根本不会编译。
  • @PeterJ,没关系。Bjorn 给了我足够的信息。这种解决了我的问题,我将很快回答我自己的问题。
  • @PeterJ sn-p 被编写为不编译,以表明如果直接调用“私有”函数,编译器会发出警告。如果您希望我这样做,我可以向 private_func 添加一个 NULL arg,但 OP 明白了这一点。显然,你没有……
【解决方案3】:

@Bjorn A. 在上面的帖子中所写的内容实际上解决了我的问题,因为编译器对以下消息感到愤怒:'_func': redefinition; different basic types 如果我尝试直接调用 _func。

这是采用的例子:

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...
}

ma​​in.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;
}

编辑:实际上,@Raymond Chen 提出了更好的函数阴影解决方案 - 想法是用括号将函数名称括起来以阻止预处理器扩展它。更多关于here的信息。

这是最终的(希望如此)解决方案,就像一个魅力:

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...
}

ma​​in.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;
}

非常感谢!干杯!

【讨论】:

  • 不客气。 :) 请注意,“不同的基本类型”警告是因为 _func() 返回 void。如果它返回 int,编译器可能没有警告。
猜你喜欢
  • 2020-09-18
  • 1970-01-01
  • 2021-03-11
  • 1970-01-01
  • 2017-05-29
  • 2010-09-08
  • 2021-03-21
  • 2014-06-23
  • 1970-01-01
相关资源
最近更新 更多