我能否重新定义一个函数或者检查它是否存在?

12

我有一个关于(重新)定义函数的问题。我的目标是创建一个脚本,在其中可以选择是否定义函数。 像这样:

void func(){}

int main(){
   if (func)func();
}

不使用函数,只需:

int main(){
   if (func)func();
}

有人有想法吗?


你的语法不通顺。你是想进行强制转换吗? - user195488
预处理器是一个选项吗?那么你可以根据另一个函数是否被定义来定义一个函数,但它只会在编译时决定。 - Paul
你不能使用预编译语句吗?或者你想要动态定义方法?我不确定你能否做到这一点。 - Lee Louviere
@monkey,这是一个快速设置,无需转换。 - Tim
@Tim 只使用函数指针。 - Lee Louviere
@Tim:我们需要澄清一下。你的术语有点不准确,也许最好你能描述一下你更广泛的目标? - Lightness Races in Orbit
8个回答

21

使用GCC的弱函数属性扩展可以实现此功能:

void func() __attribute__((weak)); // weak declaration must always be present

int main() {
  if (func) func();
  // ...
}

// optional definition:
void func() { ... }

即使 func() 在另一个 .c 文件或库中定义,这个方法仍然有效。


2
哦,看起来其他一些编译器也支持使用 #pragma weak。http://en.wikipedia.org/wiki/Weak_symbol - JAB
哈!严格来说不是完全的[c]答案,但它确实是OP正在寻找的,而我的答案则解决了这一缺陷。 - Lightness Races in Orbit
2
@R,我在自己的一些代码中以这种方式使用它;在Linux上似乎可以工作。GCC无法优化掉if (func),除非在同一编译单元中实际存在定义,因为它不知道是否会由其他编译单元或共享库提供定义等。 - bdonlan
2
@Brian:这就是注释和文档的用途。 - JAB
1
@JAB 个人而言,我习惯于看到文档解释代码的作用,而不是解释我正在查看的代码为什么不是C语言。 - Rag
显示剩余7条评论

6

我认为应该是这样的。我没有太多使用函数指针的经验,所以语法可能略有错误。

  void func()
  {
#define FUNC_PRESENT
      // code
  }

  void (*funcptr)();

#ifdef FUNC_PRESENT
      funcptr = func;
#else
      funcptr = NULL;
#endif

  int main()
  {
      if (funcptr)
          funcptr();
  }

仍然不能像他想要的那样进行动态操作。他必须根据运行时条件进行检查,并手动设置函数指针。 - Lee Louviere
1
我认为#define并不是你想象中的那样。以#开头的行会在编译开始前按顺序扫描并从源文件中删除,因此当你到达#ifdef时,FUNC_PRESENT将始终被定义。而在函数定义中放置#define FUNC_PRESENT没有任何有用的意义。 - Rag
4
虽然我建议小心将 #define 定义放在函数定义的语法内部。我知道您试图将代码捆绑在一起,以便移动时更少出错,但这可能会让某些程序员误解 PP 宏定义只在调用 func() 时才会发生。[编辑:或者会让程序员误认为这是您的想法,就像上面的 Brian 一样] - Lightness Races in Orbit
啊,Xaade说得对;你可以通过创造性的链接使其动态化,但这样你的预处理器技巧就会消失。尽管如此,虽然这不是一个准备好的产品,可以直接使用,但我认为你的方法是正确的。 - Lightness Races in Orbit
1
@Brian:Tomalak明白我的意思了。我将#define放在函数定义内部,这样当函数定义从代码/头文件中移除时,#include的代码会自动带走#define - JAB

4

使用函数指针,根据运行时条件动态设置它们,并检查空指针或将它们包装在方法中,让方法替你进行检查。

这是我所能想到的C语言中的唯一选项。

在C++中,您可以结合模板和DLL在运行时动态定义。


3

你能够“选择是否定义一个函数”唯一的方法是使用C预处理器指令。例如:

#ifdef some_name
void func() {
    do_whatever();
}
#else
  //the else part is optional
#endif

要设置这些“变量”,您可以使用#define some_name

问题是,所有这些都需要在编译时(实际上是在之前)完成,因此不能像您的示例中那样使用if语句进行操作。如果您想要一个if语句来控制程序流程,请直接使用它,不必费心尝试重命名函数或使用函数指针等。


这可能是最恰当的答案 - C语言并不适合处理非常动态的事情。 - hugomg
+1 相关: https://dev59.com/8FfUa4cB1Zd3GeqPMOXB - leonbloy

1

介绍

我猜你正在尝试做这个:

  • 两个模块,a.ob.o
  • b.o 包含了 void foo() 的定义
  • a.o 只有在最终可执行文件中链接了 b.o 才会调用 void foo()

这对于一个“插件”系统可能很有用。


变量 1

您可以使用函数指针来模拟它。我不太懂 C 语言,无法编写出符合规范的代码,但伪代码如下:

a.h

extern collectionOfFuncPtrs_t list;
int addFuncPtr();

a.c

#include "a.h"

collectionOfFuncPtrs_t list;
int addFuncPtr(FuncPtr p) {
   - add func ptr to list
   - return 0
}

int main() {
   - loop through list of function pointers
   - call functions through them
}

b.c

#include "a.h"

void bar() { /* ... */ }

static int dummy = addFuncPtr(&bar);

c.c

#include "a.h"

void ayb() { /* ... */ }

static int dummy = addFuncPtr(&ayb);

结论

现在,您可以根据需要链接b.o和/或c.o,并且int main()仅在它们存在时调用bar()和/或ayb()


变化2

如果这个主题看起来对你有用,可以尝试一些变化。特别是,如果你只有一些有条件定义的函数,你可以使用一堆单独的函数指针而不是一些列表:

a.h

extern fptr_t bar_ptr, ayb_ptr;

a.c

#include "a.h"

int main() {
   if (bar_ptr)
      bar_ptr();
   if (ayb_ptr)
      ayb_ptr();
}

b.c

#include "a.h"

void bar() { /* ... */ }

fptr_t bar_ptr = &bar;

b_dummy.c

#include "a.h"
fptr_t bar_ptr = 0;

c.c

#include "a.h"

void ayb() { /* ... */ }

fptr_t ayb_ptr = &ayb;

c_dummy.c

#include "a.h"
fptr_t ayb_ptr = 0;

结论

现在链接b.ob_dummy.o;以及链接c.oc_dummy.o

希望你至少能够理解大意...!


注释

在C++中,您可以使用std::map和构造函数轻松编写模块注册系统,这使得这个过程变得容易得多


@JAB:只是提醒一下,那不是赋值。 :) - Lightness Races in Orbit
除了我使用的术语之外,像 static int dummy = addFuncPtr(&bar);fptr_t bar_ptr = &bar; 这样的东西在 C 中会变成 static int dummy = addFuncPtr(bar);fptr_t bar_ptr = bar;,因为函数的处理方式不同。 - JAB
@JAB:嗯,公平地说,这在C语言中似乎也是这种情况。 - Lightness Races in Orbit
我明白了。虽然我从未真正看到过以&lol格式使用它,但这可能是因为在你不知道它是实际的函数名称还是指向一个函数的指针的情况下,在某些情况下,如果不小心,你可能会出现段错误(http://codepad.org/SnmFE025)。 (这很可能是在一个函数将另一个函数作为其参数之一的情况下出现问题,因此你可以传递函数名称或函数指针。) - JAB
@JAB:我真的不认为有区别。除了通过字符串在词法上传递“函数名”以外,没有其他方式可以传递函数名而不是函数指针。[编辑:哦,我明白你的意思了。嗯,取函数指针的地址是完全不同的事情。我承认这里可能存在一些容易出错的因素。但仍然不存在“传递函数名”的说法;你所说的是传递函数指针与传递指向函数指针的指针之间的区别。] - Lightness Races in Orbit
显示剩余2条评论

0

用C语言?只能使用预处理器,如其他答案所述。

C语言不像Python一样是一种动态语言。


0

如果我理解你的问题正确,C语言中正确的做法是使用函数指针。你可以获取一个函数的地址,将其赋值给一个变量,测试它是否为空等等。然而,纯粹的C语言并不是一种非常动态的语言;你可能最好使用另一种语言。


0

如果您不介意使用编译器特定的扩展,您可以使用__if_exists

#include <iostream>
using namespace std; 

// uncomment the following, and it'll still work
void maybeFunc(){ cout << "running maybe" << endl; }

int main(){
    cout << "hi!" << endl; 
    __if_exists(maybeFunc)
        cout << "maybe exists!" << endl; 
        maybeFunc(); 
    }
}

这在 MSVC 中默认工作,在使用 -fms-extensions 标志的情况下也适用于 Clang。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接