Функция, которая возвращает указатель на функцию для различных типов возвращаемых данных. - PullRequest
2 голосов
/ 15 июня 2019

Контекст этого вопроса вращается вокруг строго типизированного генетического программирования.

Я хотел бы вернуть указатель на функцию из функции, но эти указатели на функции указывают на функции с различными типами возвращаемых значений. В другом вопросе переполнения стека ( указатели на функции с разными типами возврата C ) был предложен тип возвращаемого объединения, однако я борюсь с реализацией.

Я довольно новичок в C ++, поэтому, пожалуйста, прости мое невежество, если оно показывает.

#include <iostream>
#include <string>

float Add(float a, float b) { return a + b; }
bool IfElse(bool a) { if (a) { return true; } else { return false; }; }

union return_type
{
    float(*ffptr)(float, float);
    bool(*bfptr)(bool);
};  

union fptr(std::string OPERATION) {
    if (OPERATION == "Add") {
        return_type.ffptr = Add;
    } else if (OPERATION == "IfElse") {
        return_type.bfptr = IfElse;
    }

    return return_type;
}

int main() {
    std::cout << fptr("Add") << std::endl
    return 0;
}

Я ожидаю (или, скорее, хотел бы), чтобы это напечатало адрес функции Добавить

Ответы [ 3 ]

4 голосов
/ 15 июня 2019

TL; DR версия: я думаю, что вы, возможно, пытаетесь вбить решение в решение проблемы . Рекомендуется использовать что-то вроде Шаблон посетителя , чтобы отделить проблему, чтобы вам не нужно было знать тип данных.

A union не тип. Это тип типов, например class или struct. Чтобы использовать return_type, вы должны сделать объект, который будет return_type. Это значит

union fptr(std::string OPERATION) {
    if (OPERATION == "Add") {
        return_type.ffptr = Add;
    } else if (OPERATION == "IfElse") {
        return_type.bfptr = IfElse;
    }

    return return_type;
}

должен выглядеть как

return_type fptr(std::string OPERATION) {
    return_type result; // make a return_type object
    if (OPERATION == "Add") {
        result.ffptr = Add; // set a member of the object
    } else if (OPERATION == "IfElse") {
        result.bfptr = IfElse;
    }

    return result; // return the object
}

Тогда вы можете

int main() {
    std::cout << fptr("Add").ffptr(10,20) << std::endl; // call the stored function
    return 0;
}

Большая проблема с union s - это знать, что в них. Вы можете безопасно использовать ffptr, если ffptr был последним установленным членом.

int main() {
    std::cout << fptr("Add").bfptr(true) << std::endl;
    return 0;
}

скомпилируется, но при запуске не будет хорошо себя вести. То, что произойдет, не определено, но велика вероятность, что это не будет красиво.

Вы должны быть абсолютно уверены, что функция, сохраненная в объединении, является правильной. Если ваш компилятор обновлен, вы можете использовать std::variant, чтобы помочь здесь. По крайней мере, он скажет вам, что вы движетесь в неправильном направлении, выдав исключение

#include <iostream>
#include <string>
#include <variant>

float Add(float a, float b) { return a + b; }
bool IfElse(bool a) { if (a) { return true; } else { return false; }; }

using return_type = std::variant<float (*)(float a, float b), bool (*)(bool a)>;

return_type fptr(std::string OPERATION) {
    return_type result;
    if (OPERATION == "Add") {
        result = Add;
    } else if (OPERATION == "IfElse") {
        result = IfElse;
    }

    return result;
}

int main() {
    std::cout << std::get<float (*)(float a, float b)>(fptr("Add"))(10,20) << std::endl;
    try
    {
        std::cout << std::get<bool (*)(bool a)>(fptr("Add"))(true) << std::endl;
    }
    catch (const std::bad_variant_access& e)
    {
        std::cout << e.what() << std::endl;
    }
        return 0;
}

Но в конце концов это все же не так уж и полезно. Я думаю, вам может быть полезен шаблон Visitor или один из его друзей.

2 голосов
/ 15 июня 2019

Вы были близки.Будьте осторожны, чтобы не связать объявление имени типа (union) и возвращаемого значения функции.Поскольку вы хотите сослаться на адрес указателя, я добавил void * в ваше объединение (fpaddr), чтобы вы могли четко определить, что вы печатаете адрес.Обратите внимание, что ваш fptr («Добавить») возвратил объединение, и вам нужно было четко определить, какую интерпретацию объединения вы хотели.

#include <iostream>
#include <string>

float Add(float a, float b) { return a + b; }
bool IfElse(bool a) { if (a) { return true; } else { return false; }; }

//typedef //in C you would use 'typedef'
union fp_return_t
{
    float(* ffptr )(float, float);
    bool(* bfptr )(bool);
    void* fpaddr;
}; //fp_return_t; //in C you would give the name here

fp_return_t fptr(std::string OPERATION) {
    fp_return_t fp_return;
    if (OPERATION == "Add") {
        std::cout << "Add:" << (void*) Add << std::endl;
        fp_return.ffptr = Add;
    } else if (OPERATION == "IfElse") {
        std::cout << "IfElse:" << (void*) IfElse << std::endl;
        fp_return.bfptr = IfElse;
    }

    return fp_return;
}

int main() {
    std::cout << fptr("Add").fpaddr << std::endl;
    return 0;
}
1 голос
/ 15 июня 2019

Я не совсем уверен, какова конечная цель, но вот как вы можете заставить вышеупомянутый скомпилировать и напечатать адрес функции (добавлена ​​печать простого Add для сравнения):

#include <iostream>
#include <string>

float Add(float a, float b) { return a + b; }
bool IfElse(bool a) { return a; }

union return_type
    {
    float(*ffptr)(float, float);
    bool(*bfptr)(bool);
    };  

union return_type fptr(std::string OPERATION) {
    union return_type r;
    if (OPERATION == "Add") {
        r.ffptr = Add;
    } else if (OPERATION == "IfElse") {
        r.bfptr = IfElse;
    }

    return r;
}

int main()
{
    /*the void*-cast is technically nonportable but it's hard to
      print fn-pointers portably*/
    std::cout << reinterpret_cast<void*>(Add) << '\n';

    /*you should know which union member is active: */
    std::cout << reinterpret_cast<void*>(fptr("Add").ffptr) << '\n';
    /*^should be the same address*/
    return 0;
}
...