【问题标题】:A function that returns a function pointer for functions of different return types为不同返回类型的函数返回函数指针的函数
【发布时间】:2019-06-14 22:16:52
【问题描述】:

这个问题的背景围绕着硬类型的基因编程。

我想从一个函数返回一个函数指针,但这些函数指针指向具有不同返回类型的函数。在另一个堆栈溢出问题 (Function Pointers with Different Return Types 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;
}

我希望(或者更愿意)这样打印函数 Add 的地址

【问题讨论】:

  • 您使用了正确的标头和命名空间,因此您领先于很多人,无论是否是 C++ 新手。赞一个。
  • 是“添加”运行时参数吗?
  • 我不这么认为。我会说不,因为它被硬编码到 main.
  • 你打算如何使用fptr的结果?
  • 以上是最小的sn-p。在实际代码中,它存储在一个类中并用于定义计算树。

标签: c++ pointers unions


【解决方案1】:

TL;DR 版本:我认为您可能正在尝试hammer a solution into fitting a problem。考虑使用Visitor pattern 之类的东西来解耦问题,这样您就不需要知道数据的类型。

union 不是类型。 It's a type of types, like a class or a 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;
}

unions 的最大问题是知道其中的内容。如果ffptr 是最后一个成员集,您只能安全地使用ffptr

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

将编译,但运行时表现不佳。会发生什么是不确定的,但很有可能它不会很漂亮。

您必须绝对确定存储在联合中的函数是正确的。如果你的编译器是最新的,you can use 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 pattern 或其一位朋友更有帮助。

【讨论】:

  • 你已经预料到我的下一个问题了。非常感谢。
  • @JakeDyson 很抱歉,我没有比做一些不同的事情更好的解决方案。如果不是访问者,也许如果您返回了一个派生自通用类型的function object(所以它们看起来都一样)并且知道如何从树中的节点获取它需要的值以及执行计算。
【解决方案2】:

你很亲密。 注意不要将(联合)类型名称的声明和函数返回值混为一谈。由于您要引用指针地址,所以我在您的联合(fpaddr)中添加了一个 void*,因此您可以清楚地确定您正在打印一个地址。请注意,您的 fptr("Add") 返回了联合,您需要消除对联合的解释的歧义。

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

【讨论】:

  • 谢谢您,不幸的是,您的回复来得太晚了,无法确定为答案,但您仍然发现了错误。我对 void 在两个答案中的目的感到困惑,介意详细说明吗?如果不是另一天的SO帖子:)
  • operator void * 通过将函数指针转换为 &lt;&lt; 无法显示的匿名指针来给出提示。消除一页左右的编译器诊断信息。
  • 选择的答案更“准确”,但我添加了void* fpaddr 以使用联合来执行演员表,主要是因为它对更广泛的观众来说是清楚的,并说明你应该限定使用哪个工会成员/类型。
  • 我对编译器诊断的数量有误。 g++ 吐出一行,几乎可读。 警告:'float Add(float, float)' 的地址永远不会为 NULL 可能没有多大意义,但至少它不是令人生畏的文字墙。
  • 是的,您需要知道会员类型。这就是为什么您通常会看到这些联合是结构的一部分(至少在 C 中)。因此,您可能会看到在 C++ 中使用类继承解决了这个问题,其中每个函数类都将派生自一个(可能是抽象的)基类,而 C++ 确定分配哪个派生类(以及函数)。
【解决方案3】:

我不太确定最终目标是什么,但您可以通过以下方式编译并打印函数地址(打印一个普通的 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;
}

【讨论】:

  • 看准了先生,谢谢! union return_type r;是我的金鹅。
  • @JakeDyson 没问题。 return_type r; 在 C++ 中也能正常工作,顺便说一句(普通 C 需要在联合名称前加上 union 关键字)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-04
  • 2014-05-12
  • 1970-01-01
  • 2021-05-30
  • 1970-01-01
  • 1970-01-01
  • 2015-04-18
相关资源
最近更新 更多