从使用-fno-rtti编译的共享库中子类化类

8

我正在尝试从一个使用了-fno-rtti编译的共享库进行子类化。不幸的是,我的代码库中的其他库需要-frtti。因此,由于超类没有typeinfo结构体,我得到了链接错误。

在正常编译中收到的错误:

out.o: in function typeinfo for MyClass:myclass.cpp(.data.rel.ro.<cpp magic>): error: undefined reference to 'typeinfo for NetlinkListener'

我想要继承的类是libsysutils中的一个Android类(为了节省空间进行了一些剪裁):

class NetlinkListener : public SocketListener {
    char mBuffer[64 * 1024];
    int mFormat;

public:
    static const int NETLINK_FORMAT_ASCII = 0;
    static const int NETLINK_FORMAT_BINARY = 1;

    NetlinkListener(int socket);
    NetlinkListener(int socket, int format);
    virtual ~NetlinkListener() {}

protected:
    virtual bool onDataAvailable(SocketClient *cli);
    virtual void onEvent(NetlinkEvent *evt) = 0;
};

我的存根看起来像这样:

class MyClass: public NetlinkListener {

public:
    MyClass();
    virtual ~MyClass();

    int start();
    int stop();

protected:
    virtual void onEvent(NetlinkEvent *evt);
};

MyClass中的所有方法都已实现(为空的存根)

我无法编译-shared library -frtti。有没有什么方法可以解决这个问题?


你能否提供一个示例代码,展示你如何尝试对超类进行子类化? - Tuxdude
@Tuxdude 已添加了头文件。子类没有实现(只有存根)。超类的代码是开源ASOP。 - Jon L
2个回答

5

1) 对于简单的情况,您可以创建一个没有RTTI构建的接口的C包装器。然后,在启用RTTI的程序中,您可以将C接口视为来自您的RTTI启用程序的抽象C类型。

2) 编译带有RTTI的库正是您应该做的事情(或者要求供应商这样做),除非有非常好的理由禁用RTTI(例如,您正在一个不应使用异常的域中工作,例如内核、驱动程序或其他无异常区域——或内存很紧张的地方)。

3) 修改您的库以不使用dynamic_cast、exceptions、typeid操作符或导致问题的任何内容,并重新编译并禁用RTTI。与1类似,您可以根据程序的组织方式将其制作为单独的抽象库。

4a) 下一个选项是永远不引用对象的类型信息(例如不进行dynamic_cast或throw)-这可能会很麻烦。这将消除引用类型信息的链接器错误。

4b) 可能最容易的方法是创建一个内部类(假设必须重写方法,并且必须与rtti-dependent程序进行接口)。您可以创建一个类型(inner),它从其库的类型继承并执行必要的覆盖,但然后通过一些其他类层次结构进行回调(其他层次结构可以自由使用rtti)。现在,内部类的虚拟导出被放置在禁用rtti的TU中(因为它将否则隐式引用其基类的类型信息)。然后,您可以轻松地隔离类型信息依赖关系并构建一个层次结构,该层次结构使用诸如exceptions之类的东西-该层次结构将该inner类型作为值使用。当然,如果那起作用,这全都是实现定义-您需要了解针对所针对的平台如何构造RTTI和vtables的结构(请参阅ABI refs)。即使省略RTTI也是违反标准C++的。没有信息表明符号的存在将导致正确构建编译没有这些功能的基础的vtables和类型信息。

话虽如此,1和2是您的安全选项,3属于无RTTI平台扩展的范畴(安全),4是一种方法,可以在某些系统上自由工作或不工作。

说明4b

class MyClass // << cast me. throw/catch me. get my mangled name,
              //    but put my family's virtual exports in a TU with RTTI enabled
: public MyRTTIEnabledFamily {
public:
    MyClass() : d_inner(*this) {}
    virtual ~MyClass();
private:
    void cb_onEvent(NetlinkEvent * evt) {
        // no-rtti suggests exceptions may not be available,
        // so you should be careful if your program throws.
        someInfo = evt->getInfo();
    }
private:
    // non-rtti hierarchy
    class t_inner : public NetlinkListener {
    public:
        t_inner(MyClass& pMyClass) : NetlinkListener(), d_myClass(pMyClass) {
        }

        virtual ~t_inner(); // << put your virtual exports in a TU with RTTI disabled.
                            //    one out of line virtual definition is necessary for most compilers
    private:
        virtual void onEvent(NetlinkEvent * evt) {
            // how the callback to your imp actually happens
            this->d_myClass.cb_onEvent(evt);
        }
    private:
        MyClass& d_myClass;
    };
private:
    t_inner d_inner; // << don't do anything with my type info -- it does not exist.
};

我研究了解决方案1,但问题在于有一个virtual void onEvent(...)方法,我的子类需要重写,并且将由超类定义的方法(onDataAvailable)调用。我该如何编写包装器,才能成功获取此回调? - Jon L
你的解决方案4b似乎可以处理这种情况。你能给我一些桩代码,告诉我如何设置这种继承关系吗? - Jon L
@JonL 对的 - 你仍然需要在 C 接口后面进行子类化,然后创建一个基本的回调接口。向客户端返回回调的基本形式是 typedef void (*t_netlink_event_callback)(void* pContextInfo, NetlinkEvent * pEvent); - 然后在你的重写中使用他们传递的上下文信息来调用客户端的 C 函数指针。ill.code: - justin
class t_my_subclass : NetlinkListener { virtual void onEvent(NetlinkEvent* pEvent) { this->d_netlinkEventCallbackProc(this->d_netlinkEventCallbackProcContextInfo, pEvent); }private: t_netlink_event_callback d_netlinkEventCallbackProc; void* d_netlinkEventCallbackProcContextInfo; }; 当然,客户端还需要一种方法来指定 proc 和 context 信息。 - justin

0

传递-fno-rtti只会禁用dynamic_casttypeid功能,根据gcc的文档。您应该能够派生类并使用虚拟方法而不会出现任何问题。

undefined reference to typeinfo for class错误通常会出现在将函数声明为virtual但没有提供定义时。

实际上,我在AOSP中看到NetlinkHandler.hNetlinkHandler.cpp正好做着您试图做的事情,我没有发现这些文件与您发布的代码片段之间有任何区别。


我的代码需要使用-frtti编译,因为另一个库需要dynamic_cast函数。AOSP NetlinkHandler在vold(libvold)中使用-fno-rtti进行编译,这与libsysutils相匹配,因此它没有这个问题。 - Jon L

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