【问题标题】:Where are the function address literals in c++?c ++中的函数地址文字在哪里?
【发布时间】:2010-05-20 21:40:47
【问题描述】:

更新:经过一些额外的阅读,我真正想要的是保证早期绑定(应该转换为立即调用非虚拟函数和非 PIC 代码),这可以通过将(成员)函数作为模板参数传递。我遇到的问题是 gcc = 4,5 和 vs2008 可以很好地处理这些模板参数调用。

首先,也许 literals 不是这个概念的正确术语,但它是我能想到的最接近的术语(不是作为一等公民的功能意义上的字面量)。

这个想法是,当您进行常规函数调用时,它会编译成如下内容:

callq <immediate address>

但是如果你使用函数指针进行函数调用,它会编译成这样的:

mov    <memory location>,%rax
callq  *%rax

这一切都很好。但是,如果我正在编写一个模板库,它需要带有指定参数列表的某种回调,并且该库的用户应该知道他们想要在编译时调用什么函数?然后我想编写我的模板以接受 函数文字作为模板参数。 所以,类似于

template <int int_literal> 
struct my_template {...};` 

我想写

template <func_literal_t func_literal>
struct my_template {...};

并将 my_template 中对 func_literal 的调用编译为 callq &lt;immediate address&gt;。

C++ 中是否有为此提供的工具,或者是否有解决方法来实现相同的效果?如果没有,为什么不呢(例如一些灾难性的副作用)? C++0x 或其他语言怎么样?

【问题讨论】:

  • 这个主题非常有用,因为它允许在运行时交换不同的功能。例如,DOS API 使用 ID 映射到函数。这允许 DOS 功能在对可执行文件的影响最小的情况下进行更改。只需更改查找表中的内容(函数地址)即可。
  • 函子与模板的结合是编译时的。大多数 STL 都是这样工作的,或者您的问题是否有任何警告?
  • @pmr 警告是该解决方案应该对用户代码没有影响;如果他们不使用函数对象,我不想强​​迫他们使用函数对象。
  • @Thomas Matthews 我想你误解了我的意思,调用应该编译为callq &lt;immediate address&gt;,应该有没有运行时灵活性。
  • @academicRobot:如果他们自己不使用函数对象,boost::function(&amp;foo) 之类的技巧可以利用函数指针生成函数对象。

标签: c++ generics templates assembly


【解决方案1】:

如果您在模板中使用函数指针类型并使用固定函数对其进行实例化,则编译器应直接调用该函数指针调用。

【讨论】:

  • +1 刚试了一下,效果很好。接受任意类的成员函数(不考虑虚函数)怎么样?你的回答给了我一些想法……
【解决方案2】:

CodeProject.com:

我在多个平台上使用过:http://www.codeproject.com/kb/cpp/FastDelegate.aspx

在搜索结果中看到,将显示为:http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx

...或者这不是你要找的那种东西?

【讨论】:

  • 第一个链接仍然使用指针调用,认为它们比成员函数调用更快。不知道第二个,将研究它...
  • 之前没有看到您打算阅读第二个链接。我在一分钟前玩过它,没有骰子。其实,有点奇怪。有一个内部结构可以很好地嵌入调用(在立即模式下),但它本身是通过函数指针调用的。认为这是在委托之间获得互操作所必需的,这不是这里的要求。
【解决方案3】:
#include <iostream>                                                             

template<void F()>                                                              
struct CALLER                                                                   
{                                                                               
  static void do_call()                                                         
  {                                                                             
    std::cout << __PRETTY_FUNCTION__ << std::endl;                              
    F();                                                                        
  };                                                                            
};                                                                              

void f()                                                                        
{                                                                               
  std::cout << __PRETTY_FUNCTION__ << std::endl;                                
}                                                                               

int main()                                                                      
{                                                                               
  CALLER<f>::do_call();                                                         
  return(0);                                                                    
}                                                                               

【讨论】:

    【解决方案4】:

    在 C++ 语言中,翻译器不会将函数名称折叠到可执行文件中,它们会丢失并永远消失。

    您可以制作函数名称与函数地址的表格。由于 C 和 C++ 是类型信息的坚持者,这可能更容易用汇编语言声明。在高级语言中,您必须为每种不同类型的函数指针提供一个表。但是,在组装中,它并不关心。尽管您可以使用带有switch 的函数来返回函数指针或执行函数。

    另一种方法是函数 ID(枚举)与函数地址。

    【讨论】:

    • 我不想在运行时使用函数名,我想在编译时使用函数名的代理项。您建议的解决方案仍在运行时使用函数指针。除非我误解,请详细说明是否是这种情况。
    【解决方案5】:

    至少如果我正确理解你的问题,这是微不足道的:

    template <class func>
    struct whatever { 
        operator()() { 
            func();
        }
    };
    

    对func(); 的调用通常最终会直接调用func(),如您所问,或者如果func() 很小,它的代码通常会内联生成。如果您想提高内联生成它的机会,您通常希望将其编写为仿函数(重载operator() 的类)而不是普通函数。作为一个普通函数,您最终会更频繁地调用实际函数(但这将是直接调用,而不是通过指针调用)。

    编辑:我不确定我在想什么,但你说得很对:这只适用于仿函数,而不是实际函数。我很抱歉。

    【讨论】:

    • 与仿函数配合使用效果很好。但不会为函数编译: int echo(int i){return i;} int main(void){whatever whateverObj;return 1;} 给出“错误:预期类型,得到'回声'”
    • 这是我尝试的第一件事,所以你不会受到我的批评:)
    • 这是正确的解决方案。请参阅std::less&lt;T&gt;,它使用完全相同的模式,并且出于相同的原因。直接调用是std::sort 击败std::qsort 的原因。
    • @MSalters 否(请参阅对 pmr 的评论)
    【解决方案6】:

    我想分享我自己的标准函数解决方案,从这里的其他答案扩展而来。这使用可变参数作为简写。作为一组 N 元模板执行此操作并不难(只是乏味)。有趣的是,对于这种模式,N 元模板更加灵活,因为不再需要嵌套结构。使用g++ -std=c++0x编译

    template <typename F>
    struct caller;
    
    template <class R, class ... A>
    struct caller<R(A ...)>{
       template <R F(A ...)>
       struct func{
          R operator()(A ... args){
             return F(args ...);
          }
       };
    };
    

    这样调用和调用的:

    int echoFunc(int i) {std::cout << "echo " << i << std::endl; return i;}
    ...
    caller<int(int)>::func<echoFunc> f;
    f(1);
    

    没有-O2 编译为两个嵌套的立即函数调用,使用-O2 对f(1) 的调用减少为对echoFunc 的立即调用。

    这也适用于成员函数,gcc>=4.5 和 vs2008。

    【讨论】:

      【解决方案7】:

      也可以将函数引用传递给模板。这会在最新的 clang (3.2) 中编译并打印“Hello World!”正如你所料:

      template<void(& f)()> struct test {
          void operator()() {
              f();
          }
      };
      void foo() {
          std::cout << "Hello World!\n";
      }
      test<foo> bar;
      int main() {
          bar();
      }
      

      不过,我不确定这与使用函数指针相比​​是否真的会有所不同。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-01-24
        • 1970-01-01
        • 2014-08-20
        • 1970-01-01
        • 1970-01-01
        • 2015-11-24
        • 2021-11-11
        • 1970-01-01
        相关资源
        最近更新 更多