1

I am porting an application from Windows to Linux. One component reads structured data from a file.

Sample Input: #10=CLOSED_POCKET(2.0, CARPET);

For every possible entity a corresponding c++ class is generated from the type definition. A factory creates the according object depending on the name of the entity (i.e CLOSED_POCKET). Afterwards the attributes are read one after another. Therefore we want to assign the members of the c++ class via the index of the current attribute.

The code works correctly on Windows compiled with Visual Studio 2010. I ported the code to Linux 10.04 (Lucid Lynx) and compiled it successfully with gcc 4.4.6 in Eclipse CDT Indigo.

Problem on Linux: When I access methods of the attributes the debugger sometimes jumps to wrong functions (resp. the function offset is not correct when function of subclasses should be called) what results in a segmentation fault.

I made a minimal example which also results in a segmentation fault (see below).

My Question is now: When Windows is able to run it successfully, what do I have to do, to run it under Linux with GCC?

I know that Downcasting virtual inherited classes is illegal according to the c++ standard (see Downcast in a diamond hierarchy ), but maybe there exists another solution to access members of instances via class scope. The virtual inheritance is needed because of the structure of the entities given from a ISO standard.

I also tought about providing an access array (MemberPtrArray) for every instance, but with about 80'000 entities read, an access over class scope would be nicer.

/*
 * MemberPointerTest.h
 */

#ifndef MAINTEST_H_
#define MAINTEST_H_

#include <string>

class BaseAttribute{
public:
    virtual void SetReal(double value);
    virtual void SetSelectName(std::string selectName);
};
class RealAttribute : public BaseAttribute{
public:
    double value;
    virtual void SetReal(double value);
};
class SelectAttribute: public BaseAttribute{
public:
    std::string selectName;
    virtual void SetSelectName(std::string selectName);
};

class BaseEntity{
public:
    BaseAttribute id;
    virtual ~BaseEntity(){}
};
class PocketEntity : virtual public BaseEntity{
public:
    RealAttribute depth;
};
class ClosedPocketEntity : virtual public PocketEntity{
public:
    SelectAttribute surfaceType;
    static BaseAttribute ClosedPocketEntity::* memberPtrArray[3];
    BaseAttribute* GetMember(unsigned int index);
};

#endif 



/* 
 * MemberPointerTest.cpp
 */

#include "MemberPointerTest.h"

void BaseAttribute::SetReal(double value){

}
void BaseAttribute::SetSelectName(std::string selectName){

}

void RealAttribute::SetReal(double value){
    this->value = value;
}
void SelectAttribute::SetSelectName(std::string selectName){
    this->selectName = selectName;
}

BaseAttribute ClosedPocketEntity::* ClosedPocketEntity::memberPtrArray[] = {
        (BaseAttribute ClosedPocketEntity::*) &PocketEntity::depth,
        (BaseAttribute ClosedPocketEntity::*) &ClosedPocketEntity::surfaceType
};
/* Tried the following alternatives:
*  &PocketEntity::depth, // cannot convert ‘RealAttribute PocketEntity::*’ to ‘BaseAttribute ClosedPocketEntity::*’ in initialization
*  (RealAttribute ClosedPocketEntity::*) &ClosedPocketEntity::depth, // invalid conversion from ‘RealAttribute ClosedPocketEntity::*’ to ‘BaseAttribute ClosedPocketEntity::*’
*/

BaseAttribute* ClosedPocketEntity::GetMember(unsigned int index){
    return &(this->*memberPtrArray[index]);
}


int main(){
    ClosedPocketEntity cpEntity;

    // Case 1: Calls SetReal of BaseAttribute
    BaseAttribute* depthPtr = cpEntity.GetMember(0);
    depthPtr->SetReal(3.0);

    // Case 2: Produces Segmentation fault
    RealAttribute* depthPtr2 = dynamic_cast<RealAttribute*>(cpEntity.GetMember(0));
    depthPtr2->SetReal(2.0); // SIGSEGV

    return 0;

}
Community
  • 1
  • 1
FSaccilotto
  • 656
  • 8
  • 13

1 Answers1

2
BaseAttribute ClosedPocketEntity::* ClosedPocketEntity::memberPtrArray[] = {
        (BaseAttribute ClosedPocketEntity::*) &PocketEntity::depth,
        (BaseAttribute ClosedPocketEntity::*) &ClosedPocketEntity::surfaceType
};

The first pointer conversion you're forcing here is invalid. From C++03 §4.11/2 Pointer to member conversion:

An rvalue of type “pointer to member of B of type cv T,” where B is a class type, can be converted to an rvalue of type “pointer to member of D of type cv T,” where D is a derived class (clause 10) of B. If B is an inaccessible (clause 11), ambiguous (10.2) or virtual (10.1) base class of D, a program that necessitates this conversion is ill-formed.

(Wording unchanged in C++11 as far as I can tell.)

&PocketEntity::depth is of type RealAttribute PocketEntity::*, so even a conversion to RealAttribute ClosedPocketEntity::* would be ill-formed, since PocketEntity is a virtual base of ClosedPocketEntity.

clang++ has this helpful error message:

error: conversion from pointer to member of class 'PocketEntity'
  to pointer to member of class 'ClosedPocketEntity'
  via virtual base 'PocketEntity' is not allowed

If you removed the virtual inheritance, the conversion is still invalid according to GCC and clang:

error: cannot initialize an array element of type
 'BaseAttribute ClosedPocketEntity::*'
with an rvalue of type
 'RealAttribute PocketEntity::*'

Nothing I can see in that section of the standard would allow this conversion (but do note that I'm out of my depth here, and might very well be missing something in the wonderful C++ conversion rules).

The compiler you used on Windows either allows this as an extension, or just happens to "do what you want" in this case. Other compilers appear to deal differently with that forced invalid cast.

As for how to fix this, I'm afraid I have no idea. (Are you sure you need such an intricate design?)

Mat
  • 202,337
  • 40
  • 393
  • 406
  • Thank you for your reply. I also tried different other linking scenarios, but it seems that the offsets can not be calculated "correctly" (as I wanted them). As this is in fact illegal according C++ I refactored the mechanism and created a memberPtrArray for every instance what works correctly with gcc and msvc. – FSaccilotto Mar 13 '12 at 06:28