Преобразование C в CPP с помощью указателей функций - PullRequest
0 голосов
/ 28 апреля 2019

В моем коде 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 ]

3 голосов
/ 28 апреля 2019

Разве это не возможно в CPP?

functionSteps[0] = (void*) filterEvenFoos;

Нет, это не так.

Это также не действительно C тоже.

Относительно преобразования функции в указатель

От https://en.cppreference.com/w/c/language/conversion#Function_to_pointer_conversion:

Любое выражение указателя функции, когда оно используется в любом контексте, кроме

  • в качестве операндаоператор адреса
  • в качестве операнда размера

подвергается преобразованию в указатель без значения на функцию, обозначенную выражением.

В нем ничего не говорится о преобразовании void* в указатель функции.

Относительно преобразований из void*

Вы можете преобразовать void* в указатель объекта.

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

Указатель на void может быть неявно преобразован в и из любого указателя на тип объекта со следующей семантикой:

  • Если указатель наОбъект преобразуется в указатель на void и обратно, его значение сравнивается равным исходному указателю.
  • Никаких других гарантий не предоставляется

Обратите внимание, что даже в этом разделе нет упоминаний о преобразовании void* в указатель на функцию.

Мне не понятно, почему ваш компьютер не сообщает об этом как об ошибке.

Вы можете использовать

functionSteps[0] = filterEvenFoos;

, если filterEvenFoos имеет правильный тип функции.Если объявление filterEvenFoos не совсем соответствует ожидаемому типу, то есть функции, которая не принимает аргументов и возвращает void*, то вы также не можете использовать это.

1 голос
/ 28 апреля 2019

Разве это не возможно в CPP?

Строго говоря, нет, из-за безопасности типов и других правил, регулирующих прототипы функций. Однако, в зависимости от ваших потребностей, этот код C может быть перенесен на C ++.

Во-первых, следует отметить, что сигнатура функции void* fn(); в C не одинакова в C ++. В C ++, чтобы получить ту же сигнатуру функции, вам нужно ввести переменные-аргументы примерно так: void* fn(...);, однако следует отметить, что вы не можете получить доступ к переменным-аргументам переносимым образом для такой сигнатуры функции.

В C ++ void* fn(); - это то же самое, что и void* fn(void); в C. Для этого, если бы ваши функции имели переменные входы в 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 ++, но это выполнимо.

Надеюсь, что это может помочь.

1 голос
/ 28 апреля 2019

Это возможно, но синтаксис немного сложен, чтобы понять правильно.Это проще, если вы используете typedef для вашего callback-function-type, например так:

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