如何将汇编代码从C++代码中删除?

4

我的开发环境是Visual Studio 2008。

我有三个不同的库。简单概括它们的行为如下:

库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)应该是我最后的选择。

现在,我需要您的帮助,是否有任何方法可以在不更改API的情况下使用任何可能的技术更改我的库1内部以消除此汇编代码?

如果您认为无法在不更改API的情况下实现,请建议对库3的代码更改最小的方式。


4
如果您知道'm_Params.size()'将始终非常小(例如<=10),那么您可以根据它切换到不同的调用,将函数指针转换为适当类型以接受参数。如果这变得非常繁琐,您可以使用boost预处理库自动迭代。如果您确实需要支持任意数量的参数,则必须使用汇编或对库2代码进行一些重写(例如使函数接受'vector<int>')。 - Tony Delroy
回应@david.pfx所说的话:您必须保留的已发布API到底是什么?如果只是Library 3,那就很简单。 - Kerrek SB
@KerrekSB API几乎就像是带有一些小函数的库1。但是非平凡情况下,需要在无数用户之间的数百个地方更改库的使用方式。这就是为什么我最初提到,要求所有用户根据我的更改来更改他们的代码将是“我的最后选择”。在我的最后选择之前,我正在寻找其他选项(如果有的话)。 - Doonyx
也许只是我自己的问题,但我仍然完全不清楚您的要求和限制。您的问题表明用户只能看到Library 3,但您的评论(以及您发布这个问题的事实)表明Library 1的内部工作方式也是公开的。我无法理解这一点,也不知道这个问题的“正确”答案应该是什么样子的。 - Kerrek SB
1
让我借用Raymond的心灵力量,看起来你的库正在尝试模仿CDECL调用的Dispatcher。最简单的解决方案是使用内置函数,但没有push和pop的内置函数。不幸的是,这不是一个微不足道的任务,您可以将汇编代码移动到外部文件中,并使用MASM进行编译。在我看来,这对于如此小的代码来说过于繁琐了。 - CodeSettler
显示剩余16条评论
4个回答

3
如果您的最终用户只能按照“Library 3”中定义的API进行调用,那么答案显然是肯定的。这个API的表面积很小,重新实现它而不需要任何汇编语言是微不足道的。这将需要对Lib 2和Lib 1进行更改。
天真的实现将是一组带有静态“Call”函数的类。还有许多其他选择。
但问题是:我们还没有看到你要展示的所有内容。如果在Library 3中提供的API非常丰富和/或泄漏,那么答案可能从“易如反掌”到“放弃并找另一份工作”不等。由您决定。
注:此答案基于您所说的内容:API Lib 3必须保持不变,但其他所有内容都可以更改。如果您需要一个解决方案,其中Lib 2和Lib 3都不改变,只有Lib 1中的汇编代码可以更改,请编辑您的问题以解释这一点。
如果是这样,就有两种选择:符合标准或依赖于实现定义的行为。[如果只针对Windows/VS,生活可能会更容易,但我在20年前就做过这件事情,只是为了使它在许多类型的Unix上可移植。它绝对可以做到。]

我想,放弃对我来说似乎是唯一的选择,然后使用一个不使用任何恶劣汇编代码的新API。 - Doonyx
API并不使用汇编语言。具体实现则有所不同。根据API,您可以进行更改。 - david.pfx
确切地说,那就是我的问题。如何做到呢?你能告诉我一个具体的解决方案吗?我还是想不出来。 - Doonyx

1

这将消除asm push & pop,但您可能最终会得到一个大的switch语句,最多可以达到256个case语句。 (可以使用宏来生成case语句吗?)

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 相对较为困难。 - Doonyx
1
@SargiEran:是的。用代码示例替换了一下;不知道会有多大帮助...可能最终会得到一个很长的switch语句。 - slashmais
typedef void (_F)(...); _F f = (_F)m_FPtr; - 只是一个观察 - 这可以预期适用于Microsoft的“stdcall”调用约定和int参数 - 根据它所替换的汇编,但比将m_FPtr强制转换为void()(int)、void(*)(int, int)等更具可移植性 - 也不那么繁琐。从标准中,“通过指向与函数定义中使用的类型不同的函数类型(8.3.5)的指针调用函数的效果是未定义的”。 - Tony Delroy
@TonyD:为什么不太便携?(顺便说一下,它是在Linux上编写和测试的) - slashmais
因为ABI可能会以与其他函数不同的方式将参数传递给指定接受"..."参数的函数-例如,如果ABI通常在寄存器中传递前几个参数,则编译器仍然可能决定生成通过堆栈传递所有参数到...函数的代码,以便可以保持stdarg/varargs实现简单(否则需要计算使用了多少个参数,并遵循ABI模型以知道从哪些寄存器检索值)。例如,在VS上,如果其中一个参数是float,它会失败。 - Tony Delroy

0

不是100%确定,但看起来你正在将值ite推到堆栈中,然后稍后将其弹出到i中。如果这就是你所做的一切,请考虑使用std::stack替换asm push/pop。类似于:

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

1
很遗憾,这个不起作用。函数没有被调用。 - Doonyx

0
你看过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 ) );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接