4

プロジェクトには、オブジェクトRationalComplex、およびを記述する3 つのヘッダー ファイルがありRubyObjectます。最初の 2 つはテンプレートです。すべては、ヘッダー ファイルで定義されているコピー コンストラクターを使用して相互Rational変換Complexできますconst RubyObject&

注:これらの定義は必然的に存在します。それらがすべてヘッダーに入ると、循環依存関係が発生します。

少し前に、ソース ファイルで定義された 2 つのコピー コンストラクターで未解決のシンボル エラーが発生しました。ソースファイルに次の関数を含めることができました

void nm_init_data() {
    nm::RubyObject obj(INT2FIX(1));
    nm::Rational32 x(obj);
    nm::Rational64 y(obj);
    nm::Rational128 z(obj);
    volatile nm::Complex64 a(obj);
    volatile nm::Complex128 b(obj);
}

次に、メイン ソース ファイルのライブラリ エントリ ポイントから呼び出しnm_init_data()ます。そうすることで、これらのシンボルが適切にリンクされました。

残念ながら、最近 GCC をアップグレードしたところ、エラーが再発しました。実際、GCC 4.6 では少し異なる場所(Travis-CI など) で発生しているようです。

しかし、これはバージョン固有の問題ではありません (前に考えていたように)。GCC 4.6 を実行するTravis CI の Ubuntu ベースのシステムで確認できます。しかし、GCC 4.8.1 または 4.8.2 を搭載した Ubuntu マシンでは表示されません。しかし、4.8.2 を搭載した Mac OS X マシンでは見られますが、4.7.2 を搭載した同じマシンではありません。最適化をオフにしても効果がないようです。

ライブラリで実行するnmと、シンボルは間違いなく未定義です。

$ nm tmp/x86_64-darwin13.0.0/nmatrix/2.0.0/nmatrix.bundle |grep RationalIsEC1ERKNS
                 U __ZN2nm8RationalIsEC1ERKNS_10RubyObjectE
00000000004ca460 D __ZZN2nm8RationalIsEC1ERKNS_10RubyObjectEE18rb_intern_id_cache
00000000004ca458 D __ZZN2nm8RationalIsEC1ERKNS_10RubyObjectEE18rb_intern_id_cache_0

未定義のシンボルに従属する定義済みのエントリが 2 つある理由はわかりませんが、コンパイラについてもあまり知りません。

Rationalまた、コピー コンストラクターは、テンプレートの各バージョンの未定義のシンボルのようです。

__ZN2nm8RationalIiEC1ERKNS_10RubyObjectE
__ZN2nm8RationalIsEC1ERKNS_10RubyObjectE
__ZN2nm8RationalIxEC1ERKNS_10RubyObjectE

「うーん、それは奇妙だ」と私は思った。"Complex64Complex128もそのnm_init_data関数で呼び出されますが、どちらも適切に解決され、nm -u出力には表示されません。" そこで、Rational のコピー構築の前にも追加volatileしてみました。最適化してほしくないものをコンパイラーが最適化していたのかもしれないと考えました。しかし、悲しいことに、それも解決しませんでした。これは、注意して行いました:

void nm_init_data() {
  volatile VALUE t = INT2FIX(1);
  volatile nm::RubyObject obj(t);
  volatile nm::Rational32 x(const_cast<nm::RubyObject&>(obj));
  volatile nm::Rational64 y(const_cast<nm::RubyObject&>(obj));
  volatile nm::Rational128 z(const_cast<nm::RubyObject&>(obj));
  volatile nm::Complex64 a(const_cast<nm::RubyObject&>(obj));
  volatile nm::Complex128 b(const_cast<nm::RubyObject&>(obj));
}

注意点は、まったく同じエラーが発生することですが、代わりに複合オブジェクトの場合です。ああ!

dyld: lazy symbol binding failed: Symbol not found: __ZN2nm7ComplexIdEC1ERKNS_10RubyObjectE
  Referenced from: /Users/jwoods/Projects/nmatrix/lib/nmatrix.bundle
  Expected in: flat namespace

dyld: Symbol not found: __ZN2nm7ComplexIdEC1ERKNS_10RubyObjectE
  Referenced from: /Users/jwoods/Projects/nmatrix/lib/nmatrix.bundle
  Expected in: flat namespace

これは完全にばかげています。関数と同じソース ファイル内のこれらの関数の両方の定義を次に示しnm_init_data()ます。

namespace nm {
  template <typename Type>
  Complex<Type>::Complex(const RubyObject& other) {
    // do some things
  }

  template <typename Type>
  Rational<Type>::Rational(const RubyObject& other) {
    // do some other things
  }
} // end of namespace nm

ヒント:nm_init_data()言及する価値のあることの 1 つは、 gets が呼び出されたとき (つまり、ライブラリが読み込まれたとき) にエラーが発生しないことです。それはずっと後になって、これらの面倒な関数への別の呼び出し中に発生します。

この問題を一度だけ修正するにはどうすればよいですか?他の人も気に入っていますか?

4

2 に答える 2

2

あなたは次のように主張していますが、私はそれを疑っています。

これらの定義は必然的に存在します。それらがすべてヘッダーに入ると、循環依存になります。

ほとんどの場合、コードを追加の .hpp ファイルに分割することで、このような循環的なもつれを解決できます。このファイルは、必要な場所にテンプレート定義を含むクラス定義と共に含まれます。

コードに実際の循環依存関係がある場合、コンパイルできませんでした。通常、依存関係が循環しているように見える場合は、よく調べてメソッド レベルに降りて、どちらが両方の型をコンパイルする必要があるかを確認する必要があります。

したがって、タイプが相互に使用され、すべてが 1 つの .cpp ファイルにコンパイルされる可能性があります (たとえば、3 つの .hpp インクルードを介して)。または、別の型へのポインターしかない場合は、前方宣言を使用して、すべてのテンプレートが解決されるようにします。または 3 番目に、順方向に依存するメソッドと逆方向に依存するメソッドがあり、1 つの種類を 1 つのファイルに入れ、他の種類を別のファイルに入れると、再び問題がなくなります。

さらに、不足しているアイテムには前方宣言を使用する必要があるようです。関数の定義後、次のようなものが期待されます。例えば:

template nm::Complex<nm::RubyObject>::Complex(const nm::RubyObject& other);
于 2013-11-22T13:16:50.073 に答える