I'm currently writing a library that has some abstract classes. In addition to checking that the library compiles, I'd like to make sure that all pure virtual methods have been defined in classes that are intended to be concrete. I had hoped that I could get this information from nm
or objdump
, but so far I haven't been able to tell.
Consider the following minimal example:
struct A
{
void f() {}
};
struct B
{
virtual void f() = 0;
};
struct C : public B
{
void f() override {}
};
When I look at the nm
output, I get the following. (I've excluded anything that doesn't relate to one of these 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
It's easy to distinguish A
(a class with no virtual methods) from B
and C
. But I want to be able to distinguish the fact that B
is an abstract class whereas C
is concrete.
Obviously, if I have a list of all pure virtual methods, and a map of the class hierarchy, then I could iterate over them and check whether they are defined. (Above, you can see that C::f
is defined, but B::f
is not.) But I was hoping that there would be an automatic way of doing this. (I was hoping that the vtable
or typeinfo
would show up differently above.)
Another way would be to add an extra file where I instantiate one object from every class that I expect to be concrete, and I will get a compiler error if any of them have undefined virtual methods. But this is also annoying.
Clearly, I'm not an expert on ELF or the C object file model in general, so I'd appreciate a useful introduction or reference if the answer turns out to be complicated.
As a programmer, I need to make sure that I have fully defined every class I expect to be instantiated.
Only you can know which classes these are.
if you have a list of such classes, you can generate a test program along the lines of:
#include <assert.h>
#include <mylib.h>
int main()
{
assert(!std::is_abstract<Class1>::value);
...
assert(!std::is_abstract<ClassN>::value);
}
compile and run it. If it doesn't assert
, you are good.
You could in theory also do this at the test.so
level. You'll need to locate the vtable
for every expected-to-be-concrete class X
, and examine the values in that table. If any value is equal to &__cxa_pure_virtual
, then the class is not concrete.
This is complicated by the fact that the vtable
s are relocated at load time. So you'd have to find the vtable
s, then find relocation records which apply to them, and then search for __cxa_pure_virtual
.
Overall, generating a test program is a much easier approach.
Update:
I was hoping that I could do something like
nm -C test.so | grep ... | ...
to select the names of pure virtual classes.
There is no way to do that.
At a glance I would be able to tell if any of them did not belong on the list.
What you could do is find all classes with virtual tables via nm -D test.so | grep ' _ZTV' | c++filt
. Use that list of all classes to generate the test program, but instead of assert
ing simply print the class name and the result of the test.
Finally filter this list to just the classes where is_abstract<T>::value
is true. VoilĂ : you now have the list you were seeking.