使用功能指针将C转换为CPP

时间:2019-04-28 03:17:19

标签: c++ c pointers casting function-pointers

在我的C代码中,有以下几行:

void* (*functionSteps[numSteps])();
functionSteps[0] = (void*) filterEvenFoos;
functionSteps[1] = (void*) timesFooByTwo,
functionSteps[2] = (void*) mapFooToBar;

哪个工作正常。我有一个函数数组,这些函数返回void *类型并接受任意数量的输入。

我试图在C ++中做同样的事情,但出现错误

assigning to 'void *(*)()' from incompatible type 'void *'

在CPP中这不可能吗?

3 个答案:

答案 0 :(得分:3)

  

在CPP中这不可能吗?

functionSteps[0] = (void*) filterEvenFoos;

不,不是。

它也不是真正有效的C。

关于函数到指针的转换

来自https://en.cppreference.com/w/c/language/conversion#Function_to_pointer_conversion

  

在除

以外的任何上下文中使用的任何函数指示符表达式      
      
  • 作为地址运算符的操作数
  •   
  • 作为sizeof的操作数
  •   
     

转换为表达式所指定函数的非左值指针。

它没有说明将void*转换为函数指针。

关于来自void*的转化

您可以将void*转换为对象指针。

来自https://en.cppreference.com/w/c/language/conversion#Pointer_conversions

  

可以使用以下语义将指向void的指针隐式转换为对象类型的任何指针:

     
      
  • 如果将对象的指针转换为指向void并返回的指针,则其值将等于原始指针。
  •   
  • 不提供其他担保
  •   

即使在该部分中,也请注意,没有提到将void*转换为函数指针。

我不清楚您的计算机为什么不将其报告为错误。

您可以使用

functionSteps[0] = filterEvenFoos;

如果filterEvenFoos是正确的函数类型。如果filterEvenFoos的声明与期望的类型不完全匹配,即不带任何参数并返回void*的函数,则您也不能使用它。

答案 1 :(得分:1)

有可能,但是语法有点麻烦。如果将typedef用作回调函数类型,则更容易,例如:

#include <stdio.h>

void* filterEvenFoos() {return NULL;}
void* timesFooByTwo() {return NULL;}
void* mapFooToBar() {return NULL;}

typedef void* (*VoidFunction)();

int main(int, char **)
{
   const int numSteps = 3;

   VoidFunction functionSteps[numSteps];
   functionSteps[0] = filterEvenFoos;
   functionSteps[1] = timesFooByTwo;
   functionSteps[2] = mapFooToBar;
}

答案 2 :(得分:1)

  

在CPP中这不可能吗?

严格来说,不是,因为类型安全和其他控制功能原型的规则。但是,根据您的需要,可以将C代码移植到C ++。

首先,应该注意的是,C中void* fn();的功能签名在C ++中是不同的。在C ++中,要获得相同的函数签名,您需要像这样引入variadic argumentsvoid* fn(...);,但是,应注意,对于这样的函数签名,您不能随便访问可变参数。 >

在C ++中,void* fn();与C中的void* fn(void);相同。为此,如果函数在C中具有变量输入,则需要使用可变参数在C ++中做一些额外的工作列表。

例如,如果您的代码类似于以下C代码:

#include <stdio.h>
#define NUM_STEPS 3

static void* filterEvenFoos(void)
{
    printf("42\n");
    return NULL;
}

static void* timesFooByTwo(int val)
{
    printf("%d\n", (val * 2));
    return NULL;
}

static void* mapFooToBar(double obj1, size_t obj2)
{
    printf("foo(%f)->bar(%zu)\n", obj1, obj2);
    return NULL;
}

int main(void) 
{
    void* (*functionSteps[NUM_STEPS])();
    functionSteps[0] = (void*)filterEvenFoos;
    functionSteps[1] = (void*)timesFooByTwo;
    functionSteps[2] = (void*)mapFooToBar;

    functionSteps[0]();
    functionSteps[1](42);
    functionSteps[2](3.14, &main);
    return 0;
}

您可以通过多种方式将其移植到C ++,但是可以使用va_arg功能来获取变量输入,如下所示:

#include <iostream>
#include <cstdarg>
#include <vector>

static void* filterEvenFoos(int na, ...)
{
    std::cout << 42 << std::endl;
    return NULL;
}

static void* timesFooByTwo(int na, ...)
{
    va_list vl;
    va_start(vl, na);
    std::cout << ((va_arg(vl, int)) * 2) << std::endl;
    va_end(vl);
    return NULL;
}

static void* mapFooToBar(int na, ...)
{
    va_list vl;
    va_start(vl, na);
    double obj1 = va_arg(vl, double);
    size_t obj2 = va_arg(vl, size_t);
    std::cout << "foo(" << obj1 << ")->bar(" << obj2 << ")" << std::endl;
    va_end(vl);
    return NULL;
}

int main(int argc, char* argv[])
{
    std::vector<void* (*)(int, ...)> functionSteps;
    functionSteps.push_back(&filterEvenFoos);
    functionSteps.push_back(&timesFooByTwo);
    functionSteps.push_back(&mapFooToBar);

    functionSteps[0](0);
    functionSteps[1](0, 42);
    functionSteps[2](0, 3.14, &main);
    return 0;
}

您可能会注意到,函数签名略有变化,以允许以可移植的方式访问每个函数中的可变参数。

如果您使用的是C ++ 11,则还可以使用std::function内的vector,但是仍然需要具有匹配的功能签名。

您还可以使用类和继承,或模板专门化,但是在您的情况下,这些可能会变得非常致命。

最后,它不是从C代码到C ++的直接端口,但这是可行的。

希望可以提供帮助。