【问题标题】:How to remove this assembly code from C++ code?如何从 C++ 代码中删除此汇编代码?
【发布时间】:2014-07-05 10:57:49
【问题描述】:

我的环境是 Visual Studio 2008。

我有 3 个不同的库。概括起来,它们的行为等价如下。

库 1 - 提供要注册的功能

class FunctionRegistry
{
   typedef std::list<int> TListInt;
   TListInt m_Params;
   void * m_FPtr;
public:
   FunctionRegistry(void * fptr):m_FPtr(fptr){}
   FunctionRegistry& Insert(int value){m_Params.push_back(value); return *this;}
   void Call();
};

void FunctionRegistry::Call()
{
    /*this is the area I want to get rid of */
    TListInt::iterator ite = m_Params.begin();
    while (ite != m_Params.end());
    {
       __asm push *ite;//I want to remvoe this assmebly code somehow
       ++ite;//Edited after initial post
    }

    ((void(*)())m_FPtr)();

    ite = m_Params.begin();
    while (ite != m_Params.end());
    {
        int i = 0;
        __asm pop i;//I want to remvoe this assmebly code somehow
        ++ite;//Edited after initial post
    }
    /*end of area to get rid of*/
}

Library 2 - 定义一组函数,如

void foo(int i, int j)
{
    std::cout << "foo int int " << i << " " << j << std::endl;
}

void bar(int i)
{
    std::cout << "bar int " << i << std::endl;
}

void biz()
{
    std::cout << "biz" << std::endl;
}

FunctionRegistry fRegFoo((void*)&foo);
fRegFoo.Insert(10).Insert(20);

FunctionRegistry fRegBar((void*)&bar);
fRegBar.Insert(10);

FunctionRegistry fRegBiz((void*)&biz);

库 3 - 这是上述两个库的用户,示例用法如下,

fRegFoo.Call();
fRegBar.Call();
fRegBiz.Call();

这是一个遗留代码,我现在正试图摆脱上述汇编代码。我可以轻松更改(通过一些 API 更改)我的库 1 来满足我的需要。但是我的问题是,由于库在大量用户中的大量分布,要求他们根据库 1 的 API 更改来更改他们的用例(这里是库 3)应该是我最后的选择。

现在,我正在寻求您的帮助,有没有什么方法可以使用任何可能的技术来更改我的 library 1 内部结构,而无需更改其 API 以消除此汇编代码?

如果您认为不更改任何 API 就无法实现,请建议对库 3 的代码更改最少的方法。

【问题讨论】:

  • 如果你知道 m_Params.size() 总是相当小(比如 vector<int>)。
  • 呼应@david.pfx 所说的:您必须保留的已发布API 到底是什么?如果它只是图书馆 3,那是微不足道的。
  • @KerrekSB API 几乎与库 1 类似,但功能较少。但不平凡的情况是到处改变图书馆的使用。在数百个跨越大量用户中。这就是为什么我最初提到,要求我的所有用户根据我的更改更改他们的代码,这将是“我最后的选择”。我正在寻找最后一个选项之前的选项(如果有的话)。
  • 可能只是我,但我仍然完全不清楚您的要求和限制。您的问题表明用户只能看到 Library 3,但您的 cmets(以及您发布此问题的事实)表明 Library 1 的内部工作方式也以某种方式公开。我无法理解这一点,也不知道这个问题的“正确”答案应该是什么样子。
  • 让我借用Raymond 的精神力量,看来您的库正在尝试为模仿CDECL 调用Dispatcher。最直接的解决方案是使用内在函数,但 push 和 pop 没有内在函数。不幸的是,这不是一项简单的任务,您可以将 asm 代码移动到外部文件并使用 MASM 进行编译。对于这么小的代码,IMO 太多了。

标签: c++ visual-studio assembly function-pointers


【解决方案1】:

如果您的最终用户只能调用“库 3”中定义的 API,那么答案显然是肯定的。这个 API 的表面积很小,在没有任何汇编语言的情况下重新实现它是微不足道的。这将需要对 Lib 2 和 Lib 1 进行更改。

天真的实现将是一组具有静态Call 函数的类。还有很多其他选择。

但问题是:我们还没有看到您必须展示的所有内容。如果库 3 中提供的 API 很丰富,并且/或者如果它有漏洞,那么答案可能是从“轻松愉快”到“放弃并找到另一份工作”。您的来电。


注意:此答案基于您所说的:API Lib 3 不得更改,但其他一切都可以。相反,如果您需要一个解决方案,其中 Lib 2 和 Lib 3 都未更改,并且只有 Lib 1 中的汇编代码可以更改,请编辑您的问题以解释这一点。

如果是这样,则有两种选择:要么符合标准,要么依赖于实现定义的行为。 [如果是 Windows/VS,生活可能会更轻松,但我在 20 年前就做了这件事,只是为了让它可以移植到多种 Unix 上。绝对可以做到的。]

【讨论】:

  • 我想,放弃似乎是我的选择,并附带一个不使用任何讨厌的汇编代码的新 API。
  • API 不使用汇编。实施确实如此。您可以根据 API 进行更改。
  • 没错,这是我的问题。怎么做。你能告诉我一个具体的解决方案吗?还是想不通。
【解决方案2】:

这摆脱了 asm push & pop 但你最终可能会得到一个大的 switch 语句,最大为 256 case-statements。 (可以使用宏来生成案例陈述吗?)

void FunctionRegistry::Call()
{
    typedef void (*_F)(...);
    _F f = (_F)m_FPtr;

    auto p=*m_Params.begin();
    switch(m_Params.size())
    {
        case 0: f();break;
        case 1: f(p); break;
        case 2: f(p, p+1); break;
        //...
        //case 256: f( ...please get therapy... ); break
    }
}

【讨论】:

  • 在您提供的链接中,在选定的答案中,它是编译时函数绑定。我需要运行时函数绑定机制。另一个答案是使用可变参数,这会改变我的 API。我在上面的帖子和我的评论中提到了为什么改变我的 API 相对困难。
  • @SargiEran:是的。替换为代码示例;不知道这会有多大帮助......可能最终会有一个大转变
  • typedef void (*_F)(...); _F f = (_F)m_FPtr; - 只是一个观察 - 这可以预期适用于 Microsoft 的“stdcall”调用约定和 int 参数 - 根据它要替换的程序集,但便携性明显降低 - 而且更少乏味 - 比将 m_FPtr 强制转换为 void(*)(int)void(*)(int, int) 等。从标准“通过指向函数类型 (8.3.5) 的指针调用函数的效果与使用的类型不同函数的定义未定义”。
  • @TonyD:为什么不那么便携? (顺便说一下,它是在 Linux 上编写和测试的)
  • @slashmais:因为 ABI 可能将参数传递给指定为接受“...”参数的函数,这与其他函数不同 - 例如,如果 ABI 通常传递寄存器中的前几个参数,编译器可能仍然决定生成通过堆栈将所有参数传递给... 函数的代码,以便 stdargs/varargs 实现可以保持简单(否则它需要计算它使用了多少参数并遵循 ABI 模型来知道哪些寄存器从中检索值)。例如,在 VS 上,如果其中一个参数是 float,则会失败。
【解决方案3】:

不是 100% 肯定,但看起来您将值 ite 推送到堆栈,然后稍后将其弹出到 i。如果这就是您所做的一切,请考虑将 asm push/pop 替换为 std::stack。比如:

#include <stack>
//...
void FunctionRegistry::Call()
{
    std::stack<TListInt::value_type> astack;
    TListInt::iterator ite = m_Params.begin();
    while (ite != m_Params.end());
    {
       astack.push(*ite);
       ++ite;//Edited after initial post
    }

    ((void(*)())m_FPtr)();

    ite = m_Params.begin();
    while (ite != m_Params.end());
    {
        int i = astack.pop();
        ++ite;//Edited after initial post
    }
}

【讨论】:

  • 不幸的是,这不起作用。没有调用函数。
【解决方案4】:

你看过 boost::bind 吗? 像这样:

#include <boost/bind/bind.hpp>
class FunctionRegistry
{
  typedef void tProcedure (void);

  private:
    const tProcedure proc;

  public:
    explicit FunctionRegistry( tProcedure proc ) : proc(proc) {}
    void Call() const { proc(); }

};

const FunctionRegistry fRegFoo( boost::bind( foo, 10, 20 ) );

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-04
    • 2011-08-27
    • 2022-11-20
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 2012-06-16
    • 2019-11-30
    相关资源
    最近更新 更多