模仿C#&x27;新';(隐藏虚拟方法)在C++;代码生成器 我正在开发一个系统,它采用一组编译的.NET程序集并发出C++代码,然后将其编译成具有C++编译器的任何平台。当然,这涉及到一些各样的欺骗,因为各种各样的事情.NET没有C++。

模仿C#&x27;新';(隐藏虚拟方法)在C++;代码生成器 我正在开发一个系统,它采用一组编译的.NET程序集并发出C++代码,然后将其编译成具有C++编译器的任何平台。当然,这涉及到一些各样的欺骗,因为各种各样的事情.NET没有C++。,c++,code-generation,C++,Code Generation,其中一种情况是能够隐藏虚拟方法,例如C#中的以下方法: 我想出了一个解决这个问题的办法,这个办法看起来很聪明,而且很有效,如下例所示: namespace impdetails { template<class by_type> struct redef {}; } struct A { virtual void MyMethod( void ); }; struct B : A { virtual void MyMethod( void ); }; struc

其中一种情况是能够隐藏虚拟方法,例如C#中的以下方法:

我想出了一个解决这个问题的办法,这个办法看起来很聪明,而且很有效,如下例所示:

namespace impdetails
{
template<class by_type>
struct redef {};
}

struct A
{
    virtual void MyMethod( void );
};

struct B : A
{
    virtual void MyMethod( void );
};

struct C : B
{
    virtual void MyMethod( impdetails::redef<C> );
};

struct D : C
{
    virtual void MyMethod( impdetails::redef<D> );
};
我不担心额外的源代码开销;该系统的输出主要不用于人类消费

不幸的是,这实际上会导致运行时开销。直观地说,由于
impdetails::redef
是空的,它不会占用任何空间,传递它也不会涉及任何代码

<>但是,C++标准,出于我理解但不完全同意的理由,要求对象不能具有零大小。这就给我们留下了一个编译器实际发出代码来创建和传递对象的情况

事实上,至少在VC2008上,我发现它甚至遇到了将虚拟字节归零的麻烦,即使在发布版本中也是如此!我不知道为什么这是必要的,但这让我更不想这样做

如果所有其他方法都失败,我可以随时更改函数的实际名称,例如可能有
MyMethod
MyMethod$1
、和
MyMethod$2
。然而,这会导致更多的问题。例如, $>代码>在C++标识符中实际上是不合法的(虽然我已经测试了编译器允许它)。在输出程序中一个完全可接受的标识符也可以是输入程序中的标识符,这意味着需要一个更复杂的方法,使这个选项不太吸引人。 事实证明,在这个项目中还有其他一些情况,如果能够使用任意类型参数修改方法签名,那将是一件好事,类似于我将类型传递给
impdetails::redef
的方式


有没有其他聪明的方法来解决这个问题,或者我是不是在每个调用站点增加开销还是在修改名称之间徘徊?

在考虑了系统的一些其他方面之后,比如.NET中的接口,我开始想,也许根本不必使用C++虚拟调用机制。我认为,使用这种机制的梅西耶正在变得越来越快。

在这种方法中,每个用户对象类都有一个单独的vtable结构(可能保存在一个单独的命名空间中,如
vtabletype::
。生成的类将有一个指针成员,该成员将通过一些技巧进行初始化,以指向vtable的静态实例。虚拟调用将显式使用该vtable中的成员指针

如果操作得当,这应该与编译器自己的实现具有相同的性能。我已经在VC2008上确认了这一点。(相比之下,仅使用straight C,这是我之前计划的,可能不会执行得很好,因为编译器经常将
优化到寄存器中。)

手动编写这样的代码会很糟糕,但这当然不是生成器需要考虑的问题。这种方法在该应用程序中确实有一些优势:

  • 因为这是一种更加明确的方法,所以我们可以更加确定它所做的正是.NET指定它应该做的关于
    newslot
    以及选择接口实现的事情

  • >p>它可能比传统的C++接口更有效(依赖于一些内部细节),它倾向于调用多重继承。

  • 在.NET中,当对象的
    .ctor
    运行时,对象被认为是完全构造的。这会影响虚拟函数的行为。在明确了解vtables的情况下,可以通过在分配过程中写入它来实现。(尽管将
    .ctor
    代码放入普通成员函数是另一种选择。)

  • 在实现反射时,它可以避免冗余数据

  • 它提供了更好的对象布局控制和知识,这对垃圾收集器很有用

从下侧看,VC++表条目完全失去了C++编译器的重载特性:这些条目是数据成员,不是函数,所以没有过载。在这种情况下,只需对成员编号就行了(如:<代码> 0 0<代码>,<代码>这在调试时可能不会太糟糕,因为一旦跟随指针,您将看到一个实际的、正确命名的成员函数


我想我最终可能会这样做,但无论如何,我想听听是否有更好的选择,因为这是一个公认的相当复杂的方法(也是一个问题)。

为什么不给函数一个不同的名称?同样的效果——从“基”更改其签名函数--但不添加参数。@CoryNelson最坏情况是,我始终可以修改方法名。不过,这确实会导致一些问题。例如,输出程序中的任何合法标识符在输入程序中也是合法的,这意味着如果生成器使用一些微不足道的修改,则生成的名称可能不明确n、 更复杂的转换可以避免这种歧义,但理想情况下,我正在尝试避免这种混乱。我还有其他类似的情况(与泛型相关),在这些情况下,标识符可能最终必须包含非平凡的类型信息,这将使混乱进一步复杂化。
namespace impdetails
{
template<class by_type>
struct redef {};
}

struct A
{
    virtual void MyMethod( void );
};

struct B : A
{
    virtual void MyMethod( void );
};

struct C : B
{
    virtual void MyMethod( impdetails::redef<C> );
};

struct D : C
{
    virtual void MyMethod( impdetails::redef<D> );
};
C *c_d = &d;
c_d->MyMethod( impdetails::redef<C>() );