Discussion:
Using pointer-to-member of incomplete type in constructor call in VS2008
(too old to reply)
Mihajlo Cvetanović
2010-02-03 12:17:04 UTC
Permalink
I made a minimal VS2008 project that compiles cleanly in Debug build but
reports Run-Time Check Failure #0 during execution. The problem is that
Data class is defined in one place and just declared in another. It
seems that sizeof(pointer-to-member) is different in these two cases. If
the order of includes is changed in Test.cpp OR in Configuration.cpp OR
there is #include "Data.h" instead of struct Data; in Configuration.h
then there is no stack corruption. Is this a known bug (or a bug at all)?

=== Test.cpp ============================
#include "stdafx.h"
#include "Data.h"
#include "Configuration.h"

Configuration global_object(&Data::member);

int _tmain(int argc, _TCHAR* argv[])
{
Data data;
data.member = 42;

int member = data.*(global_object.member_pointer);

return 0;
}
=========================================

=== Configuration.cpp ===================
#include "stdafx.h"
#include "Configuration.h"
#include "Data.h"

Configuration::Configuration(int Data::* member_pointer) :
member_pointer(member_pointer)
{
}
=========================================

=== Configuration.h =====================
struct Data;

class Configuration
{
public:
Configuration(int Data::* member_pointer);
int Data::* member_pointer;
};

=========================================

=== Data.h ==============================
struct Data
{
int member;
};

=========================================
Igor Tandetnik
2010-02-03 12:59:44 UTC
Permalink
Post by Mihajlo Cvetanović
I made a minimal VS2008 project that compiles cleanly in Debug build but
reports Run-Time Check Failure #0 during execution. The problem is that
Data class is defined in one place and just declared in another. It
seems that sizeof(pointer-to-member) is different in these two cases. If
the order of includes is changed in Test.cpp OR in Configuration.cpp OR
there is #include "Data.h" instead of struct Data; in Configuration.h
then there is no stack corruption. Is this a known bug (or a bug at all)?
MSVC compiler implements an optimization whereby the size of a pointer-to-member depends on the complexity of the class hierarchy (off the top of my head, 4 bytes when only single inheritance is present, 8 with multiple inheritance, 12 with virtual inheritance and 16 to support incomplete classes).

This optimization is, technically, illegal. For one thing, the C++ standard says that if you reinterpret_cast a pointer-to-member to another unrelated pointer-to-member type and then back, the value should remain unchaged, which in practice means that all such types have to be the same size. Then there are cases like yours, where the same pointer-to-member type is used with incomplete class at one point in the program, and with complete class in another.

You can give the compiler a hint as to the complexity of your class, or turn off this optimization completely, with a compiler switch, a #pragma, or a special keyword. See

http://msdn.microsoft.com/en-us/library/yad46a6z.aspx
http://msdn.microsoft.com/en-us/library/83cch5a6.aspx
http://msdn.microsoft.com/en-us/library/ck561bfk.aspx
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Mihajlo Cvetanović
2010-02-03 15:24:45 UTC
Permalink
Post by Igor Tandetnik
MSVC compiler implements an optimization whereby the size of a pointer-to-member depends on the complexity of the class hierarchy (off the top of my head, 4 bytes when only single inheritance is present, 8 with multiple inheritance, 12 with virtual inheritance and 16 to support incomplete classes).
Ah, so it's by design. Then I wish that linker would detect the problem
and stop the linking process if two compilation units have different
representations of the same pointer-to-member type. If reinterpret_cast
is involved then there should be a linker warning if there are different
representations of any two pointer-to-member types.

Thanks for the links.
Mihajlo Cvetanović
2010-02-04 14:29:32 UTC
Permalink
Update on the subject. I was playing with inheritance keywords
__single_inheritance, __multiple_inheritance, __virtual_inheritance. If
you specify the correct keyword everything works. If you specify
incorrect inheritance the compiler reports C2292. Very nice. BUT if you
specify virtual inheritance for non-virtual inheriting class then
compiler also hits internal error. Not nice. Someone should report this :-)

So, the compiler has a way to detect inconsistencies in handling the
pointers to members between compilation units. One wonders why then
there is no some /vma (A for automatic) option as a mix of /vmb and /vmg
so that all pointers to members are assumed to be of one type in all
cases except when the class for specific pointer to member is defined
before the pointer itself. "/vma /vms" could then be default option and
all would be happy.
Igor Tandetnik
2010-02-04 16:05:47 UTC
Permalink
Post by Mihajlo Cvetanović
Update on the subject. I was playing with inheritance keywords
__single_inheritance, __multiple_inheritance, __virtual_inheritance.
If you specify the correct keyword everything works. If you specify
incorrect inheritance the compiler reports C2292. Very nice. BUT if
you specify virtual inheritance for non-virtual inheriting class then
compiler also hits internal error. Not nice. Someone should report this :-)
So, the compiler has a way to detect inconsistencies in handling the
pointers to members between compilation units.
Are you sure it was between translation units? I would be very surprised if the compiler were able to do that. I'm pretty sure the compiler actually saw a forward declaration with an incorrect keyword (perhaps in an #included header), and then a mismatched definition, in the same translation unit.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Mihajlo Cvetanović
2010-02-05 11:22:39 UTC
Permalink
Post by Igor Tandetnik
Are you sure it was between translation units? I would be very surprised if the compiler were able to do that. I'm pretty sure the compiler actually saw a forward declaration with an incorrect keyword (perhaps in an #included header), and then a mismatched definition, in the same translation unit.
You're right, it's not between compilation units, that was just my
wishful thinking. That's probably why compiler doesn't have something
like /vma, and we're all free to choose our own way on how to circumvent
MS's design.

Loading...