如何检查 C/C++ 中是否存在函数

How to check if a function exists in C/C++?

本文关键字:C++ 是否 存在 函数 何检查 检查      更新时间:2023-10-16

在我的代码中的某些情况下,我最终只有在定义了该函数时才调用该函数,否则我不应该调用该函数。我怎样才能做到这一点?

like:
if (function 'sum' exists ) then invoke sum ()

也许问这个问题的另一种方式是如何确定函数是否在运行时定义,如果是,则调用?

当你声明"sum"时,你可以这样声明它:

#define SUM_EXISTS
int sum(std::vector<int>& addMeUp) {
    ...
}

然后,当您使用它时,您可以:

#ifdef SUM_EXISTS
int result = sum(x);
...
#endif

我猜你来自一种脚本语言,其中所有事情都是在运行时完成的。C++要记住的主要事情是两个阶段:

  • 编译时
    • 预处理器运行
    • 模板代码转化为真正的源代码
    • 源代码以机器代码上交
  • 运行
    • 机器代码运行

所以所有的#define和类似的事情都发生在编译时。

....

如果你真的想在运行时完成这一切......你可能有兴趣使用一些组件架构产品。

或者,也许一种插件类型的架构就是你所追求的。

使用 GCC,您可以:

void func(int argc, char *argv[]) __attribute__((weak)); // weak declaration must always be present
// optional definition:
/*void func(int argc, char *argv[]) { 
    printf("FOUND THE FUNCTIONn");
    for(int aa = 0; aa < argc; aa++){
        printf("arg %d = %s n", aa, argv[aa]);
    }
}*/
int main(int argc, char *argv[]) {
    if (func){ 
        func(argc, argv); 
    } else {
        printf("did not find the functionn");
    }
}

如果您取消注释 func,它将运行它,否则它将打印"未找到该函数"。

虽然其他回复是有用的建议(dlsym,函数指针,...(,但你不能编译引用不存在的函数C++代码。至少,必须声明函数;如果不是,则代码将无法编译。如果没有任何东西(编译单元,一些对象文件,一些库(定义函数,链接器就会抱怨(除非它很弱,见下文(。

但你真的应该解释你为什么要问这个。我猜不出来,有办法实现你未说出的目标。

请注意,dlsym通常需要没有名称重整的函数,即声明为 extern "C"

如果使用 GCC 在 Linux 上编码,则还可以在声明中使用 weak 函数属性。然后,链接器会将未定义的弱符号设置为 null。

附录

如果你

从某个输入中获取函数名称,你应该知道只有函数的子集应该以这种方式调用(如果你不小心调用任意函数,它会崩溃!(,并且你最好显式构造该子集。然后,您可以使用 std::mapdlsym(子集中的每个函数都声明为 extern "C" (。请注意,具有NULL路径的dlopen提供了主程序的句柄,您应该与主程序链接-rdynamic以使其正常工作。

您确实希望仅按其名称调用适当定义的函数子集。例如,您可能不想以这种方式称呼abortexitfork

铌。如果你动态知道被调用函数的签名,你可能想使用 libffi 来调用它。

我怀疑海报实际上是在寻找更多类似于SFINAE检查/调度的东西。使用C++模板,可以定义模板函数,一个调用所需函数(如果存在(,另一个不执行任何操作(如果函数不存在(。然后,您可以使第一个模板依赖于所需的函数,以便在函数不存在时模板格式不正确。这是有效的,因为C++模板替换失败不是错误(SFINAE(,因此编译器将回退到第二种情况(例如,它什么都不做(。

请参阅此处的一个很好的例子:是否可以编写一个模板来检查函数是否存在?

使用指向函数的指针。

 //initialize
 typedef void (*PF)();
 std::map<std::string, PF> defined_functions;
 defined_functions["foo"]=&foo;
 defined_functions["bar"]=&bar;
 //if defined, invoke it
 if(defined_functions.find("foo") != defined_functions.end())
 {
     defined_functions["foo"]();
 }

如果你知道要调用的函数在哪个库中,那么你可以使用dlsym()dlerror()来确定它是否存在,以及指向该函数的指针是什么。

编辑:我可能不会真正使用这种方法 - 相反,我会推荐Matiu的解决方案,因为我认为这是更好的做法。但是,dlsym()不是很出名,所以我想我会指出来。

您可以使用

#pragma weak来支持它的编译器(请参阅弱符号维基百科条目(。

此示例和注释来自共享库和动态加载的内幕故事:

#pragma weak debug
extern void debug(void);
void (*debugfunc)(void) = debug;
int main() {
    printf(“Hello Worldn”);
    if (debugfunc) (*debugfunc)();
}

可以使用弱杂注强制链接器忽略未解析的问题 符号 [..] 程序编译并链接是否 debug(( 实际上在任何对象文件中定义。当符号保留时 未定义,链接器通常将其值替换为 0。所以,这个 技术可以是程序调用可选代码的有用方法 这不需要重新编译整个应用程序。

所以另一种

方法,如果你使用 c++11 是使用函子:

您需要将其放在文件的开头:

#include <functional>

函子的类型以以下格式声明:

std::function< return_type (param1_type, param2_type) >

您可以添加一个变量来保存总和的函子,如下所示:

std::function<int(const std::vector<int>&)> sum;

为了方便起见,让我们缩短参数类型:

using Numbers = const std::vectorn<int>&;

然后,您可以使用以下任一字符填充函子 var:

一个λ:

sum = [](Numbers x) { return std::accumulate(x.cbegin(), x.cend(), 0); } // std::accumulate comes from #include <numeric>

函数指针:

int myFunc(Numbers nums) {
    int result = 0;
    for (int i : nums)
        result += i;
    return result;
}
sum = &myFunc;

"绑定"创造了一些东西:

struct Adder {
    int startNumber = 6;
    int doAdding(Numbers nums) {
        int result = 0;
        for (int i : nums)
            result += i;
        return result;
    }
};
...
Adder myAdder{2}; // Make an adder that starts at two
sum = std::bind(&Adder::doAdding, myAdder);

最后使用它,它是一个简单的if语句:

if (sum)
    return sum(x);

总之,函子是指向函数的新指针,但它们更通用。如果编译器足够确定,实际上可能会内联,但通常与函数指针相同。

当与std::binb和lambda结合使用时,它们比旧式C函数指针要优越得多。

但请记住,它们在 c++11 及以上环境中工作。(不是 C 或 C++03(。

在C++中,用于检查成员是否存在的技巧的修改版本应该在编译时而不是运行时为您提供所需的内容:

#include <iostream>
#include <type_traits>
namespace
{
    template <class T, template <class...> class Test>
    struct exists
    {
        template<class U>
        static std::true_type check(Test<U>*);
        template<class U>
        static std::false_type check(...);
        static constexpr bool value = decltype(check<T>(0))::value;
    };
    
    template<class U, class = decltype(sum(std::declval<U>(), std::declval<U>()))>
    struct sum_test{};
    
    template <class T>
    void validate_sum()
    {
        if constexpr (exists<T, sum_test>::value)
        {
            std::cout << "sum exists for type " << typeid(T).name() << 'n';
        }
        else
        {
            std::cout << "sum does not exist for type " << typeid(T).name() << 'n';
        }
    }
    
    class A {};
    class B {};
    
    void sum(const A& l, const A& r); // we only need to declare the function, not define it
}
int main(int, const char**)
{
    validate_sum<A>();
    validate_sum<B>();
}

以下是使用 clang 的输出:

sum exists for type N12_GLOBAL__N_11AE
sum does not exist for type N12_GLOBAL__N_11BE

我应该指出,当我使用 int 而不是 A 时发生了奇怪的事情(sum()必须在 sum_test 之前声明才能使exists工作,所以也许exists不是正确的名称(。某种模板扩展,当我使用 A 时似乎不会引起问题。

这个答案是针对全局函数的,作为对测试方法的其他答案的补充。此答案仅适用于全局函数。

首先,在单独的命名空间中提供一个回退虚拟函数。然后在模板参数内确定函数调用的返回类型。 根据返回类型,确定这是回退函数还是所需函数。

如果禁止在函数的命名空间中添加任何内容,例如 std:: 的大小写,则应使用 ADL 在测试中找到正确的函数。

例如,std::reduce()是 c++17 的一部分,但应该支持 c++17 的早期 gcc 编译器没有定义std::reduce()。以下代码可以在编译时检测是否声明了std::reduce。在编译资源管理器中查看它在这两种情况下都能正常工作。

#include <numeric>
namespace fallback
{
    // fallback
    std::false_type reduce(...) { return {}; }
    // Depending on
    // std::recuce(Iter from, Iter to) -> decltype(*from)
    // we know that a call to std::reduce(T*, T*) returns T
    template <typename T, typename Ret = decltype(reduce(std::declval<T*>(), std::declval<T*>()))>
    using return_of_reduce = Ret;
    // Note that due to ADL, std::reduce is called although we don't explicitly call std::reduce().
    // This is critical, since we are not allowed to define any of the above inside std::
}
using has_reduce = fallback::return_of_reduce<std::true_type>;
// using has_sum = std::conditional_t<std::is_same_v<fallback::return_of_sum<std::true_type>, 
//                                                   std::false_type>,
//                                    std::false_type,
//                                    std::true_type>;
#include <iterator>
int main()
{
    if constexpr (has_reduce::value)
    {
        // must have those, so that the compile will find the fallback
        // function if the correct one is undefined (even if it never
        // generates this code).
        using namespace std;
        using namespace fallback;
        int values[] = {1,2,3};
        return reduce(std::begin(values), std::end(values));
    }
    return -1;
}

与上述示例不同,当您无法控制返回类型时,可以使用其他方法,例如 std::is_samestd::contitional

例如,假设您要测试函数 int sum(int, int) 是否在当前编译单元中声明。以类似的方式创建test_sum_ns::return_of_sum .如果该函数存在,它将int,否则std::false_type(或您喜欢的任何其他特殊类型(。

using has_sum = std::conditional_t<std::is_same_v<test_sum_ns::return_of_sum, 
                                                  std::false_type>,
                                   std::false_type,
                                   std::true_type>;

然后,您可以使用该类型:

if constexpr (has_sum::value) 
{
   int result;
   {
      using namespace fallback; // limit this only to the call, if possible.
      result = sum(1,2);
   }
   std::cout << "sum(1,2) = " << result << 'n';
}

注意:您必须具有using namespace,否则编译器将无法在if constexpr中找到回退函数并会抱怨。通常,应避免using namespace因为将来更改命名空间内的符号可能会破坏代码。在这种情况下,没有其他方法可以解决它,因此至少将其限制在尽可能小的作用域,如上例所示<</p>

div class="answers>

在 c 中,您可以使用函数指针数组

#include<stdio.h>
#include<string.h>
typedef struct i32_2arg{int a, b;}i32_2_arg;
typedef struct i32_arg{int a;}i32_arg;
void add(void*a,void*r){
  i32_2_arg* l = (i32_2_arg*)a;
  ((i32_arg*)r)->a = l->a+l->b;
}
void sub(void*a,void*r){}
char lFunNames[8][64] = {"add","sub",0};
void (*lFuns[8]) (void*,void*) = {&add,&sub,0};
void evalfun(char* lName, void* inargs,void* outargs){
  for(int i = 0; i < 8; i++ )
  if (!strcmp(lFunNames[i],lName)) (*lFuns[i])(inargs,outargs);
}
int main(){
  i32_2_arg ab ={2,3};
  i32_arg sum;
  evalfun("add",&ab,&sum);
  printf("if "add" exists, result is %dn",sum.a);
  return 0;
}