2012-03-06 7 views
1

を経由して、インスタンスのメンバーにアクセスします。 1つのコンポーネントがファイルから構造化データを読み取ります。C++、私はWindowsからLinuxへアプリケーションを移植していたクラスのスコープ

サンプル入力: #10 = CLOSED_POCKET(2.0、カーペット)。対応するC++クラスは型定義から生成される全ての可能なエンティティの

。 ファクトリは、エンティティの名前(CLOSED_POCKETなど)に応じて、対応するオブジェクトを作成します。その後、属性は次々に読み取られます。したがって、現在の属性のインデックスを使用してC++クラスのメンバーを割り当てたいと考えています。

コードはVisual Studio 2010でコンパイルされたWindows上で正しく動作します。コードをLinux 10.04(Lucid Lynx)に移植し、Eclipse CDT Indigoでgcc 4.4.6を使用して正常にコンパイルしました。

Linuxの問題: 属性のメソッドにアクセスすると、デバッガが間違った関数にジャンプすることがあります(サブクラスの関数を呼び出すときに関数のオフセットが正しくない場合)。

Iは、(下記参照)セグメンテーション障害をもたらす最小の例を作りました。

私の質問は今です:Windowsを正常に実行するには、LinuxでGCCを実行するには何が必要ですか?

私は、仮想継承したクラスをダウンキャストすると、C++標準(Downcast in a diamond hierarchy を参照)によると違法ですが、おそらくクラススコープを経由してインスタンスのメンバーにアクセスするには、別の解決策が存在することを知っています。 ISO規格から与えられたエンティティの構造のため、仮想継承が必要です。

は、私はまた、すべてのインスタンスのアクセス配列(MemberPtrArray)を提供については取り払わ、が、約80'000エンティティが読んで、クラススコープを超えるアクセスがよりよいだろう。

/* 
* 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; 

} 
+9

ああ、私......... –

答えて

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

あなたがここに強制している最初のポインタの変換は無効です。 Bクラス型である

型の右辺値「タイプCV TのBのメンバーへのポインタ」に変換することができる:C++ 03§4.11/ 2 部材変換へのポインタから型の右辺値「タイプCV TのDのメンバーへのポインタ、」Bが曖昧アクセスできない(項11)、(10.2)又は仮想(10.1)基底クラスである場合、DはBの派生クラス(項10)でありますDの場合、この の変換を必要とするプログラムは不正です。 PocketEntityが仮想であるため、

(C++ 11私の知る限りでは変わらない言葉遣い。)

&PocketEntity::depthは、さえRealAttribute ClosedPocketEntity::*への変換が悪い形成されることになるタイプRealAttribute PocketEntity::*であるので、 ClosedPocketEntityの塩基。

clang++は、この便利なエラーメッセージを表示しています

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

あなたは仮想継承を削除した場合、変換はGCCと打ち鳴らすによると、まだ無効です:

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

私はそのセクションで見ることができる何もありませんこの変換を可能にします(しかし、私はここで私の深いところから抜け出しており、すばらしいC++変換ルールでは何かが欠けているかもしれません)。

Windowsで使用しているコンパイラでは、これを拡張子として使用するか、この場合は「やりたいことをやってみる」のいずれかが行われます。他のコンパイラは、その強制的な無効キャストとは異なる扱いをしているようです。

これを修正する方法については、わかりません。 (あなたは本当にそのような複雑なデザインが必要ですか?)

+0

ありがとうございます。私はまた別のリンクシナリオを試しましたが、オフセットが「正しく」(私が望むように)計算できないようです。これは実際にはC++では違法であるため、私はそのメカニズムをリファクタリングし、gccとmsvcで正しく動作するすべてのインスタンスに対してmemberPtrArrayを作成しました。 – FSaccilotto

関連する問題