【问题标题】:Define a factory function that returns a pointer to a function created in this factory function定义一个工厂函数,它返回一个指向在这个工厂函数中创建的函数的指针
【发布时间】:2019-07-11 13:43:10
【问题描述】:

我的问题是:如何定义一个工厂函数,它接受一个参数并返回一个指向根据该参数创建的函数的函数指针? (或任何其他实现相同结果的好方法)


说明

本质上,我想定义一个工厂函数set,它接受一个std::string参数str,并返回一个指向函数pf的指针。函数指针pf 指向一个刚刚在set 中创建的函数,它接受std::ostream & 并返回std::ostream &

这样,我想用std::cout << set(str) << "text" << std::endl;打电话。我希望set(str) 的结果至少和这个语句一样长。

供您参考,来自cplusplus

ostream & operator<< (ostream & (*pf)(ostream &));


更多解释

这是一个真实的例子,如下所示。

首先我有操纵器

std::ostream & black(std::ostream & os)
{
    return os << "\033[30m";            // make terminal text black
}

std::ostream & red(std::ostream & os)
{
    return os << "\033[31m";            // make terminal text red
}

std::ostream & green(std::ostream & os)
{
    return os << "\033[32m";            // make terminal text green
}

这样当我打电话时

std::cout << red << "this text is in red" << std::endl;

我会得到想要的效果。到目前为止一切顺利。

现在有了地图

std::map<std::string, std::string> code =
{
    {"black", "30"},
    {"red", "31"},
    {"green", "32"}
    // ...
    // could extend much longer as there are many colors
};

我希望用foo("red") with 实现类似的自定义效果

void foo(std::string str)
{
    std::cout << set(str) << ("this text is in " + str) << std::endl;
}

其中set 获取"red" 并在地图code 中查找对应的代码"31"

但是我在实现set 函数时遇到了问题。如果有人能帮我解决这个问题,我将不胜感激!

两个音符:

  1. 如果可能,我希望set 函数具有良好的性能,因为它会被一次又一次地调用。

  2. 如果我想错了,请见谅 --- 只要你能实现 set 的功能,我不介意你用不同的方式来做。

感谢您阅读这篇长文。非常感谢!

【问题讨论】:

  • returns a function pointer pointing to a function that is created according to that parameter 无法在运行时创建函数。
  • 在 C++11 中使用 lambda 函数绝对是可能的。

标签: c++ c++11


【解决方案1】:

想错了。相反,您需要做的是创建自己的参数化操纵器。这样的操纵器可以实现为一个单独的类,您必须为其重载 operator&lt;&lt;

struct set_color
{
    std::string name;
    explicit set_color(std::string name)
        : name(std::move(name)) { }
    friend std::ostream & operator<<(std::ostream & os, const set_color &color)
    {
        if (color.name == "black")
            os << "\033[30m";
        else if ... // and so on
            ...
    }
}

您可以改用map 将名称转换为颜色代码,但基本思想是无需为此创建一堆函数。

【讨论】:

  • 感谢您指出我走错路了。我认为你的方法是解决我的问题的标准、直接的方法。但看起来 Marek R 的解决方案直接解决了我的问题。
【解决方案2】:

为什么要把事情复杂化?

std::string_view color_from_name(std::string_view colorName)
{
    static const std::map<std::string_view, std::string_view> colors {
        {"red"sv, "\033[31m"sv},
        {"green"sv, "\033[32m"sv},
        {"black"sv, "\033[30m"sv},
    };
    return colors.at(colorName);
}

int main() {
    std::cout << color_from_name("red") << "tada" << color_from_name("green") << "got it\n";

    return 0;
}

https://wandbox.org/permlink/nh2qMKoovh2qVlk2

【讨论】:

  • 这个有可能成为解决方案。它是否适用于像"bold_red_on_black" 这样我想解析字符串并返回"\033[1;31;40m" 的情况?我打算只保留 3 张地图(而不是一张很长的地图)。一种风格。一个用于文本。一个用于背景。
  • 对自己的回答:我想是的。那么是的,为什么我把它弄得这么复杂?我根本不需要归还机械手。我只是写了一个函数,它接受一个字符串并返回一个字符串。
  • 我喜欢这个简单的修饰符。但我可以想象一个像 std::cout &lt;&lt; color("red").bold().on("black") 这样的 API 有一个类。
  • @JoshWilson 这是个好建议。可能跑题了。但是如果人们不记得订单并且有些人可能想打电话给std::cout &lt;&lt; color("red").on("black").bold()怎么办?出于设计目的,我不希望引入过多的编码负担。
  • 当然完全取决于您的设计决定,但它很简单,可以让顺序无关紧要。只需使用在实例上设置标志的onbold 方法和return *this 创建一个类,然后让operator&lt;&lt; 读取标志并产生正确的输出。
【解决方案3】:

我认为您可以像这样全局定义 operator&lt;&lt; 的另一个重载:

using Manipulator = std::function<std::ostream&(std::ostream&)>;
std::ostream& operator<< (std::ostream& stream, Manipulator&>& func) {
    return func(stream);
}
std::map<std::string, Manipulator> manipulators;
for (const auto& val : data) {
    manipulators.emplace(std::pair<std::string, Manipulator>(val.first, 
            [&](std::ostream& out) -> std::ostream& { return out << "\033[" << val.second << "m"; }
    ));
}

如果您将字符串映射存储到 lambda 函数作为您的操纵器,您应该能够使您的 set() 函数成为返回您的 lambda 的简单包装器。

【讨论】:

  • 谢谢@Josh Wilson。这可能是一种方式。但考虑到在终端上有 7 种样式(如 bold)和 16 种文本/背景颜色,可能会有大约 7 * 16 * 16 = 1792 风格化颜色的组合。看起来您想将所有这些加载到映射到 std::functionmap 中。好的部分是一切都会在一开始就准备好。坏事可能是需要一段时间。考虑这将是库,并且您并不总是需要所有这些颜色。这是我个人的看法。
  • 同意,这会产生很高的设置成本。您可以为每次调用 set() 生成一个新的 lambda 函数,并缓存它们以供下次查找。但是@r3mux-n0x 的回答更惯用,如果您将它与您的颜色的compile-type map 结合使用,您可以消除设置时间。
猜你喜欢
  • 2020-01-12
  • 2021-07-02
  • 1970-01-01
  • 1970-01-01
  • 2011-01-12
  • 2011-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多