5

I have the following code, I am using MSVC:

template <typename T>
void CanMock() {
  class SimpleType {};
  static_assert(sizeof(void(SimpleType::*)()) == sizeof(void(T::*)()),
                "Can't mock a type with multiple inheritance or with "
                "non-polymorphic base class");
}

int main() {
  class base {};
  class derived : public base {};
  class derivedVirtual : virtual public base {};
  CanMock<derived>();
  CanMock<derivedVirtual>();
}

The static_assert in CanMock passes when called with derived but fails when called with derivedVirtual.

As far as I understand the assertion tries to compare the size of the class function pointer of a SimpleType with the size the class function pointer of derivedVirtual,

Now my questions are:

  • I understand that the size of a derivedVirtual should increase because of the vtable pointer stored in it (right?), but why does the size of derivedVirtual class function pointer increases when inheriting with virtual?
  • Why in clang the same assertion does not fail?

Just to give a little more context:

I am using FakeIt for testing. At some point I wanted to Mock a specific class, but I was not able due to the failing static_assert inside FakeIt source code. After some investigation I understood that the failure was due to the fact that the class that I was trying to mock was inheriting virtually from a base class. So I came with the above code, copy-pasted from FakeIt.

Here is the line where I copied from.

So another question that came to my mind is: which is the correlation between virtual inheritance and the ability to mock a class?

Thank you for your time.


Edit:

Here is the TEST_CASE that does not compile when I try to Mock the class:

TEST_CASE("Mock") {
    struct base {};
    struct derivedVirtual : virtual public base {
        virtual int A() = 0;
    };
    fakeit::Mock<derivedVirtual> mock; // This triggers the failing static_assert in MSVC but compiles in Clang
}

Is there a workaround to allow me to mock the class with FakeIt?

Dundo
  • 714
  • 8
  • 12
  • The standard says nothing about how virtual functions and virtual base classes should be implemented. Or the size of a pointer. Perhaps there is some tradeoff here between data size and code size? – BoP Mar 16 '23 at 10:22
  • 1
    On MSVC pointer method in a class with multiple inheritance (even without virtual inheritance) is also 16 bytes (like pointer to method in a class with virtual inheritance like `derivedVirtual`). This post is relevant: https://stackoverflow.com/questions/12006854/why-the-size-of-a-pointer-to-a-function-is-different-from-the-size-of-a-pointer. – wohlstad Mar 16 '23 at 10:45
  • "I have the following code". Did you write it? Did you take it from somewhere? If its from Catch2 source a link to source would help. Its online here https://github.com/catchorg/Catch2 – 463035818_is_not_an_ai Mar 16 '23 at 11:06
  • 1
    The `sizeof` is not measuring any vtable pointer. It's measuring the sizeof a "pointer" to member function (which really is not a pointer, it's implementation specific magic goo). Since it is implementation specific and its implementation details are not specified by the standard, there's not much that can be said about what its size should be. (I just woke up, so take my comment with a grain of salt.) – Eljay Mar 16 '23 at 11:38
  • @463035818_is_not_a_number, you are right, I just edited the question – Dundo Mar 16 '23 at 11:39
  • @wohlstad The link seems very useful, thank you. – Dundo Mar 16 '23 at 11:46
  • I tried the type traits, and derivedVirtual different from base and derived for these: is_aggregate is_empty is_literal_type is_pod is_standard_layout is_trivial is_trivially_constructible is_trivially_copy_assignable is_trivially_copy_constructible is_trivially_copyable is_trivially_default_constructible is_trivially_move_assignable is_trivially_move_constructible ... is one of them suitable for your use case? – Eljay Mar 16 '23 at 12:21
  • @Eljay I am sorry but I don't understand how the type traits are supposed to help me. I edited my question to show more clearly what I want to achieve: I would like to understand which is the correlation betweek the mockability of a class and the size of its function pointers, and I would like to understand if there is a workaround for mocking `derivedVirtual` with FakeIt. – Dundo Mar 16 '23 at 12:44
  • 1
    As per the FakeIt [documentation](https://github.com/eranpeer/FakeIt), at the bottom, it says what you are trying to do is not supported. – Eljay Mar 16 '23 at 12:52
  • Thank you for pointing it out. Now I am surprised since in Clang the code inside TEST_CASE compiles and allows me to mock the class. The problem is only with MSVC. – Dundo Mar 17 '23 at 09:05

0 Answers0