Passing a pointer to a member function across C++ modules

583 views Asked by At

Given two classes: X and Y. X contains a member of type Y * and passes the pointer to its member function to the instance of Y. So in C++ it looks like this:

x.h

#pragma once

class Y;

class X
{
public:
    X();
private:
    Y *y;
    void myfunc(int);
};

x.cpp

#include "x.h"
#include "y.h"

X::X()
{
    this->y = new Y(*this, &X::myfunc);
}

void X::myfunc(int dummy)
{
    dummy = dummy;
}

y.h

#pragma once

//#include "x.h"    // this would fix the issue!

class X;

class Y
{
public:
    Y(X &x, void (X::*pMyCallback)(int));
    ~Y();

private:
    X &x;
    void (X::*pMyCallback)(int) = nullptr;
};

y.cpp

#include "y.h"
#include "x.h"

Y::Y(X &x, void (X::*pMyCallback)(int))
    : x(x), pMyCallback(pMyCallback)
{
    (x.*pMyCallback)(3);
}

Y::~Y()
{
}

When I run this code, Visual Studio 2015 raises the exception: "MYPROGRAM.exe has triggered a breakpoint". Sometimes it crashes with Access Violation exception.

int main(int argc, char *argv[])
{
    X x;
    return 0;
}

It has come to my notice that the issue is somehow related to compilation units as it doesn't crash if I define X and Y in the same file. Moreover, if I include the declaration of X (i.e. "x.h") into "y.h", it won't crash either.

Is there a rationale for such behavior?

1

There are 1 answers

6
Justin Time - Reinstate Monica On BEST ANSWER

The issue seems to be that when defining pointers-to-members after the class is declared, but before it's defined, MSVC has to guess at the class' inheritance model. There are four ways to handle this:

  • Use the MS-specific keyword __single_inheritance, __multiple_inheritance, or __virtual_inheritance in the declaration class X; in "y.h", to force it to use a specific model. Of these, __single_inheritance is the most efficient, but only usable when the class doesn't have multiple or virtual inheritance; since X has no base classes, this is fine.

    // y.h
    
    #pragma once
    
    //#include "x.h"    // this would fix the issue!
    
    class __single_inheritance X;
    
    class Y
    {
    public:
        Y(X &x, void (X::*pMyCallback)(int));
        ~Y();
    
    private:
        X &x;
        void (X::*pMyCallback)(int) = nullptr;
    };
    
  • Use the MS-specific pragma pointers_to_members, for the same reason. Again, we can use "single inheritance", which is the most efficient choice.

    // y.h
    
    #pragma once
    
    #pragma pointers_to_members(full_generality, single_inheritance)
    
    //#include "x.h"    // this would fix the issue!
    
    class X;
    
    class Y
    {
    public:
        Y(X &x, void (X::*pMyCallback)(int));
        ~Y();
    
    private:
        X &x;
        void (X::*pMyCallback)(int) = nullptr;
    };
    
  • Change the Pointer-to-member representation option in the IDE; I'm not sure how to do this, unfortunately, since I can't find the option.

  • Specify the command-line option /vmg