【问题标题】:Macros for function composition with no or minimal overhead用于函数组合的宏,没有或最小开销
【发布时间】:2017-12-04 04:48:42
【问题描述】:

假设我们有如下函数:

void blax(T t) {
    for {...
            //Stuff
                auto variable = generator(previous_internal_value); 
            //Stuff
    }
}

在函数的深处还有另一个名为“生成器”的函数。函数“blax”可以很容易地修改为实现另一个角色,让我们将此函数称为“blax2”。

两者在实现上的唯一区别是'balx2'使用了函数generator2:

void blax2(T t) {
    for {...
            //Stuff
                auto variable = generator2(previous_internal_value); 
            //Stuff
    }
}

我希望有一个通用的方式来编写这两个函数。我想出的是这样的:

    void generic_blax(T t, std::function<int(int)> generator_impl) {
            for {...
                    //Stuff
                        auto variable = generator_impl(previous_internal_value); 
                    //Stuff
            }
        }


void blax(T t) {
    return generic_blax(t, generator)
}

void blax2(T t) {
    return generic_blax(t, generator2)
}

这个实现很好,但我不确定上述代码的效率。所以我的问题三折:

有没有办法通过宏来完成这个函数“注入”,这样我就不必在运行时对 nr 个函数和 nr 个函数调用产生惩罚?

如果不是,使用 std::function 代替函数指针是否会导致性能损失?

并且,尝试使用 inline 关键字强制内联“generic_blax”以获得更接近如果 blax 和 blax2 具有“正常”函数体时的性能是否明智?或者如果函数太大,将在 'blax_generic' 之前放置 inline 而不是内联函数(即使这只需要在两个地方发生)?

【问题讨论】:

    标签: c++ functional-programming macros metaprogramming c++17


    【解决方案1】:

    考虑使用模板,而不是使用std::function&lt;...&gt;。宏被认为是有害的,因为它们的行为与函数调用略有不同,但看起来相同。

    template <class Generator>
    void generic_blax(T t, Generator generator_impl) {
        for {...
            //Stuff
            auto variable = generator_impl(previous_internal_value); 
            //Stuff
            }
        }
    

    您甚至不必更改呼叫站点

    void blax(T t) {
        return generic_blax(t, generator)
    }
    
    void blax2(T t) {
        return generic_blax(t, generator2)
    }
    

    注意关键字 inline 不再内联有任何关系,并且它从未强制它。链接时优化可以包括跨 TU 的内联

    【讨论】:

    • 我知道 inline 不会强制内联,但我认为它仍然可以“鼓励”它......有没有不同的方法来强制内联?此外,将您的实现与建议的 IIIShamanIII 相结合,您的实现不会导致额外的代码(因为该函数作为参数传递,而不仅仅是作为模板参数传递)。 ?
    • 除了复制代码之外,您不能强制内联。这也是 Knuth 的 97% 规则的一个典型例子:你应该相信编译器会做得很好。对于某些工作负载,内联函数会更慢,因为指令缓存溢出比call / ret 更受惩罚
    • 好的...那么另一个问题呢,为什么这种方法比仅仅将函数指定为模板参数更好?
    【解决方案2】:

    您可以中继内联,大多数编译器都会这样做。

    如果您想保证零成本,您可以将生成器函数包装到仿函数中并通过以下方式修改代码:

    template <class Generator>
    void generic_blax(T t) {
       for {...
         //Stuff
         auto variable = Generator()(previous_internal_value); 
         //Stuff
       }
    }
    

    【讨论】:

    • 您不需要将它们包装在仿函数中,只需将 generator_impl 作为推导类型而不是 std::function
    • @Caleth 这种方法涉及与问题中的代码相同的内联,所以这没有意义。
    • 我不确定为什么这会被否决...有人愿意解释一下吗?
    猜你喜欢
    • 2014-09-12
    • 2015-05-22
    • 1970-01-01
    • 2017-01-11
    • 2010-10-29
    • 1970-01-01
    • 2016-11-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多