有没有一种简单的方法可以分辨出共享对象库中哪些 C++ 类 是抽象的?

Is there an easy way to tell which C++ classes are abstract from a shared object library?

我目前正在编写一个包含一些抽象 classes 的库。除了检查库是否编译之外,我还想确保所有纯虚拟方法都已在 classes 中定义,这些方法旨在具体化。我曾希望我能从 nmobjdump 那里得到这些信息,但到目前为止我还无法分辨。

考虑以下最小示例:

struct A
{
    void f() {}
};

struct B
{
    virtual void f() = 0;
};

struct C : public B
{
    void f() override {}
};

当我查看 nm 输出时,我得到以下信息。 (我排除了与这些 classes 之一无关的任何内容。)

 % nm -C test.so
0000000000001174 W A::f()
000000000000118c W B::B()
000000000000118c W B::B()
0000000000001180 W C::f()
00000000000011aa W C::C()
00000000000011aa W C::C()
0000000000003db0 V typeinfo for B
0000000000003d98 V typeinfo for C
0000000000002003 V typeinfo name for B
0000000000002000 V typeinfo name for C
0000000000003d80 V vtable for B
0000000000003d68 V vtable for C

很容易区分A(没有虚方法的class)和BC。但我希望能够区分 B 是抽象的 class 而 C 是具体的。

显然,如果我有所有纯虚方法的列表和 class 层次结构的映射,那么我可以遍历它们并检查它们是否已定义。 (在上面,您可以看到 C::f 已定义,但 B::f 未定义。)但我希望有一种自动执行此操作的方法。 (我希望 vtabletypeinfo 在上面会有所不同。)

另一种方法是添加一个额外的文件,我从每个 class 中实例化一个我希望具体的对象,如果其中任何一个具有未定义的虚拟方法,我将得到一个编译器错误。但这也很烦人。

显然,我不是 ELF 或一般 C 目标文件模型方面的专家,所以如果答案变得复杂,我将不胜感激有用的介绍或参考。

下面定义一个宏如何?

#include <iostream>
#include <type_traits>

#define TEST_CC(x) if(std::is_abstract<x>::value) std::cout << #x" is abstract \n"

struct A
{
    void f() {}
};

struct B
{
    virtual void f() = 0;
};

struct C : public B
{
    void f() override {}
};

int main()
{
    TEST_CC(A);
    TEST_CC(B);
    TEST_CC(C);
}

As a programmer, I need to make sure that I have fully defined every class I expect to be instantiated.

只有 才能知道这些 class 是哪一个。

如果您有这样的 classes 列表,您可以按照以下行生成测试程序:

#include <assert.h>
#include <mylib.h>
int main()
{
  assert(!std::is_abstract<Class1>::value);
...
  assert(!std::is_abstract<ClassN>::value);
}

编译并运行它。如果没有 assert,你很好。

你可以理论上也可以在test.so水平上做到这一点。您需要为每个 expected-to-be-concrete class X 找到 vtable,并检查 table 中的值。如果任何值等于 &__cxa_pure_virtual,则 class 不具体。

由于 vtable 是在加载时重新定位的事实,这变得很复杂。所以你必须找到 vtables,然后找到适用于它们的搬迁记录,然后搜索 __cxa_pure_virtual.

总的来说,生成测试程序是一种更简单的方法。

更新:

I was hoping that I could do something like nm -C test.so | grep ... | ... to select the names of pure virtual classes.

没有办法做到这一点。

At a glance I would be able to tell if any of them did not belong on the list.

你可以做的是通过 nm -D test.so | grep ' _ZTV' | c++filt 找到 all classes with virtual tables。使用所有 classes 的列表来生成测试程序,但不是 asserting 而是简单地打印 class 名称和测试结果。

最终将此列表过滤为仅 classes,其中 is_abstract<T>::value 为真。瞧:你现在有了你要找的列表。