6

一部のコードを共有ライブラリに移動しようとしています (スタンドアロンでコンパイルすると正常に動作します) が、クラスのインライン関数で問題が発生します。mingw/gcc v4.7.2。

問題の一部は、クラス宣言の外側でインライン関数を定義することを好むためと思われます (クラス宣言をよりすっきりと読みやすく保ちます)。私はいつもこれが受け入れられ、クラス宣言内で定義することと同等であると考えていました...しかし、常にそうであるとは限りません。問題を示す簡単なサンプルを作成しました。(明らかに、dllexport は通常、インポート/エクスポートを切り替えるマクロ内にあります。)

ヘッダ:

// Uncomment one at a time to see how it compiles with: -O2 -Winline
//#define INLINE_OPTION 1 // implicit - builds without inline warnings
#define INLINE_OPTION 2 // simple external inline - gives inline warnings
//#define INLINE_OPTION 3 // external forced inline - gives inline errors

class __attribute__((dllexport)) Dummy {
public:
    Dummy() : m_int{0} {}
    ~Dummy() {}
    #if INLINE_OPTION == 1
    int get_int() const { return m_int; }
    #else
    int get_int() const;
    #endif
    int do_something();
private:
    int m_int;
};

#if INLINE_OPTION == 2
inline int Dummy::get_int() const
{ return m_int; }
#endif

#if INLINE_OPTION == 3
inline __attribute__((always_inline)) int Dummy::get_int() const
{ return m_int; }
#endif

.cpp ファイル:

int Dummy::do_something()
{
    int i = get_int();
    i *= 2;
    return i;
}

上記のように、INLINE_OPTION == 1 (暗黙のクラス内インライン定義) を使用すると、コードは警告なしでコンパイルされます。

INLINE_OPTION == 2 (クラス外のインライン定義) を使用すると、次の警告が表示されます。int Dummy::get_int() const' can never be inlined because it uses attributes conflicting with inlining [-Winline]

INLINE_OPTION == 3 (インラインを強制しようとしている) を使用すると、上記と同じ警告が表示され、さらに次のエラーが表示されます: error: inlining failed in call to always_inline 'int Dummy::get_int() const': function not inlinable、.cpp ファイルの Dummy::do_something() 内の最初の行から呼び出されていることに関する情報とともに. これは、ライブラリ自体の中で関数をインライン化しようとしていることに注意してください! 単純なアクセサー関数の場合、これは非常に大きなオーバーヘッドになる可能性があります。

私は何か間違ったことをしていますか?クラス外の定義のインライン関数をクラス内の関数定義とは異なる方法で扱うのはgccで正しいですか? (本当にクラス宣言を乱雑にする必要がありますか?)

注: この問題は、インラインで宣言したものだけに影響するわけではありません。また、constexpr として宣言されたものや、継承が関係する場合は "= default" として宣言されたデストラクタにも影響します。

編集:

同じ結果で mingw64 / gcc v4.8.0 を試してみました。これには、オプション 1 が do_something でインライン化されないという事実が含まれていることに注意してください (アセンブラーの出力を確認しました)。そのため、オプション 1 とオプション 2 の唯一の違いは、オプション 2 のみが -Winline 警告を出すことです。

4

6 に答える 6

2

Windows で共有ライブラリを作成する方法については何も知りません。Linux/OSX では、ソース コードに特別な処理は必要ないため、共有 (.so) ライブラリと通常 (.a) ライブラリの両方を同じソースから特別な処理なしで作成できます。

シンボルを共有ライブラリにエクスポートするための特別な属性が本当に必要な場合は、単純にコードを分割することができます。

namespace implementation_details {
  class __attribute__((dllexport)) DummyBase
  {
  protected:
    DummyBase() : m_int{0} {}
    ~DummyBase() {}
    int do_something();
    int m_int;
  };
}

struct Dummy: private implementation_details::DummyBase
{
  using implementation_details::DummyBase::do_something;
  int get_int() const noexcept;
};

inline __attribute__((always_inline)) int Dummy::get_int() const noexcept
{ return m_int; }
于 2013-03-29T12:51:34.017 に答える
1

私の答えは少し不可解だったかもしれません...コードスニペットを使用して私が意味することの簡単な例を挙げましょう。

ダミー.h:

#ifndef _DUMMY_H_
#define _DUMMY_H_

class __attribute__((dllexport)) Dummy {
public:
  Dummy() : m_int{0} {}
  ~Dummy() {}
  int get_int() const;
  int do_something();
private:
  int m_int;
};

// here goes the include of the implementation header file
#include "dummy.h.impl"
#endif // _DUMMY_H_

ダミー.h.impl:

// there will be no symbol for Dummy::get_int() in the dll.
// Only its contents are copied to the places where it
// is used. Placing this in the header gives other binaries
// you build with this lib the chance to do the same.
inline int Dummy::get_int() const
{ return m_int; }

もちろん、同じヘッダー ファイル内のクラス宣言のすぐ下にインライン定義を配置することもできます。しかし、これはまだ宣言と定義の分離に違反していると思います。

ダミー.cpp:

// this method will become a symbol in the library because 
// it is a C++ source file.
int Dummy::do_something()
{
  // i would if i knew what to do...
  return 0;
}

お役に立てれば幸いです。

于 2013-03-28T17:19:24.493 に答える
1

一部の人が示唆しているように、これはコンパイラのバグではありません。C++ では、関数がインラインの場合、すべての宣言でインラインで宣言する必要があります。満たさなければならない 5 つの特性があり、そのうちの 1 つが次のとおりです。

 An inline function with external linkage (e.g. not declared static) has the following additional properties:
1) It must be declared inline in every translation unit.
...

あなたの例では、最初に関数 Dummy::get_int() をクラス定義内の非インラインとして宣言しました。関数をインラインとして再宣言できないことを意味します

ソース: http://en.cppreference.com/w/cpp/language/inline

ところで: C では、インライン指定子の動作が異なります。C では、同じ関数のインライン バージョンと非インライン バージョンの両方を宣言できます。それでも、両方を実装して、同じことを行うようにする必要があります。

于 2016-02-23T08:28:47.977 に答える
1

私が別の投稿で行った編集はうまくいかなかったようですが、とにかく、いくつかの追加の明確さが適切であるように思われるので、別のフォーラムに送信した詳細を投稿しています. 以下のコードclass Cは、この問題の回避策です。クラス全体ではなく、非インライン メンバーのみをエクスポートします。他の場所のコメントで述べたように、__declspec(dllexport)同等__attribute__((dllexport))です。

test.hpp

class __declspec(dllexport) A {
public:
    int fa() { return m; }
    int ga();
private:
    int m{0};
};

class __declspec(dllexport) B {
public:
    int fb();
    int gb();
private:
    int m{0};
};
inline int B::fb() { return m; }

class C {
public:
    int fc() { return m; }
    __declspec(dllexport) int gc();
private:
    int m{0};
};

test.cpp

#include "test.hpp"

int A::ga() { return (fa() + 1); }

int B::gb() { return (fb() + 1); }

int C::gc() { return (fc() + 1); }

これをオプション付きでコンパイルすると-std=c++11 -O2 -S -Winline(gcc v4.7.2 または v4.8.0 で mingw/ming64 を使用)、ライブラリ関数 ga、gb、および gc 用に生成されたアセンブラーは次のようになります。

が:

subq    $40, %rsp
.seh_stackalloc 40
.seh_endprologue
call    _ZN1A2faEv
addl    $1, %eax
addq    $40, %rsp
ret

GB:

subq    $40, %rsp
.seh_stackalloc 40
.seh_endprologue
call    _ZN1B2fbEv
addl    $1, %eax
addq    $40, %rsp
ret

GC:

.seh_endprologue
movl    (%rcx), %eax
addl    $1, %eax
ret

そしてあなたは警告を受け取ります:
warning: function 'int B::fb()' can never be inlined because it uses attributes conflicting with inlining [-Winline]
warning: inlining failed in call to 'int B::fb()': function not inlinable [-Winline] (called from B::gb())

fa がインライン化されていないことについての警告がなかったことに注意してください (これは予想通りだと思います)。ただし、ga、gb、gc はすべてライブラリ関数であることに注意してください。インライン関数自体をエクスポートする必要があるかどうかについてどう考えても、ライブラリ内でインラインをインライン化できない正当な理由はありません。したがって、これはコンパイラのバグだと思います。

よく評価されているコードを調べて、明示的なメンバーのみをエクスポートする方法を確認してください。たとえば、ライブラリにコンパイルされるブーストのいくつかの部分 (例: 正規表現) は、このclass A手法を使用します。これは、多くのアクセサ関数がライブラリ内でインライン化されていないことを意味します。

しかし、それはさておき、現時点での答えはclass Cテクニックです (明らかに、実際のコードでは、クラス レベルで通常行うように、エクスポートとインポートを切り替えるマクロにこれを含める必要があります)。

于 2013-03-30T00:27:54.157 に答える
0

inline int get_int() const;クラス宣言 ( )で関数をインラインで宣言しないのはなぜですか? 多分エラーがありますか?

于 2013-03-28T10:14:10.500 に答える
0

コンパイラは、dll でエクスポートする必要がある関数をインライン化できません。結局、dll にリンクされた実行可能ファイルから呼び出された場合、関数にはアドレスが必要です。ほとんどの場合、do_something からの呼び出しはインライン化されますが、一般的なケースでは不可能だと思います

于 2013-03-28T15:03:15.870 に答える