0

I'm learning the binary compatibility based on this KDE wiki, and see that

add a virtual function to a class that doesn't have any virtual functions or virtual bases

will break compatibility. Then I had a try.

Assume I want to create a FastString.dll to provide out, and here is the definition.

//FastString.h

#ifdef _DLL_EXPORT
#define LIB_EXPORT __declspec(dllexport)
#else
#define LIB_EXPORT __declspec(dllimport)
#endif

class LIB_EXPORT FastString
{
public:
    FastString(void);
    ~FastString(void);

    int length();

private:
    int m_len;
    unsigned char *m_bytes;
};

and implementation

//FastString.cpp
#include "FastString.h"

FastString::FastString(void)
{
    m_len = 0;
}

FastString::~FastString(void)
{
}

int FastString::length()
{
    printf("Function of length, string len is %d\n", m_len);
    return m_len;
}

While in the third exe file test.exe, used FastString like below

// main.cpp

#include <conio.h>
#include "FastString.h"

int _tmain(int argc, _TCHAR* argv[])
{
    FastString str;
    str.length();

    printf("Please input any key to exit...");
    _getch();
    return 0;
}

Please note: in main.cpp the included FastString.h is another file, when I added the virtual function in FastString, the modification is under FastString.dll.

They are in the same solution (compiler: VS2012), and build successfully. After that, I add a new virtual function in FastString.h.

virtual bool isEmpty();

And in FastString.cpp, I implement it with simple return

bool FastString::isEmpty()
{
    return false;
}

Then I build the FastString.dll individually, and rerun test.exe. The output is same with previous one without any error.
So, why this behavior is not broken the binary compatibility?
Per my understanding, the instance str should have a vtable pointer, and the memory layout must has been changed.
I also had a debug based on VS tool and found the str still had no _vptr, does it mean the vtable is created under compiler period, not in link period?

Leo
  • 21
  • 6

1 Answers1

0

The calling of the non-virtual function FastString::length() is layout independent and that function being defined in the DLL knows the actual object layout and finds the right member. You should be hit by layout incompatibilities by making m_len public and accessing it from outside of DLL.

While there is no general rule about where the VMT is located it is usually created by compiler and put inside some specific translation unit (*.obj) , i.e. there where the first virtual function is defined. Sometimes when for instance all virtual functions are inline more advanced strategies must be applied and they usually involve linker. But generally when using an older header compiler will have not enough hints involving presence of the VMT and will not create a VMT pointer for that object.

jszpilewski
  • 1,632
  • 1
  • 21
  • 19
  • Thanks for your comments. So do you mean the instance created at `main.cpp` is based on older header, it should have no `vtable`, right? But if I follow your said "makeing `m_len` public and accessing it`, the exe will crash, based on this, the instance should be created based on new header (in FastString.dll), how to explain this? – Leo Mar 19 '18 at 01:48
  • I have tried with that add a public member `int m_public`, initialized at construction `m_public = 0` and used it in `main.cpp`, while re-build all of them. Later, I add the public virtual function, re-build the FastString.dll **individually**, the output still is ok without any error.... why? I'm so confused now. – Leo Mar 19 '18 at 06:33
  • After introducing a VMT pointer, the `this` pointer may still point to the first data member. Try adding another data member (int etc.) at the beginning of the class. – jszpilewski Mar 19 '18 at 10:57
  • Firstly thanks your explain. And yes, I added a new public member `int m_pp` at the begin of class FastString, then rebuild the FastString.dll and re-run test.exe, it crashed. But how about your meaning `this` pointer here? I'm little understand. – Leo Mar 20 '18 at 02:40
  • VMT pointer may be the first member in an object but `this` may still point to the first "real" one defined by the programmer. Compiler will use a negative offset when it needs to get the VMT pointer. Of course it is implementation specific and details may differ from compiler to compiler so generally do not expect a program to crash in a predictable way. – jszpilewski Mar 20 '18 at 09:49
  • Got it, thanks a lot, will deep learn this later to understand more clear. Or do you have any suggested article or blog related this one? Many thanks. – Leo Mar 20 '18 at 10:08
  • As this subject is non-standardized and implementation specific formal sources are scarce but you may find quite a lot info on the Web even Wikipedia at http://en.wikipedia.org/wiki/Virtual_method_table says quite a lot about using VMT pointers even with multiple inheritance so it is a good place to start. – jszpilewski Mar 20 '18 at 14:16
  • No matter how I add member variable,there are no crash and not breaking binary compatibility. I am using qt. – Crawl.W Jul 16 '20 at 07:34