読者です 読者をやめる 読者になる 読者になる

なんとな~くしあわせ?の日記

ClojureとかAWSの設定とかをメモする技術ブログ

Importing inline functions in MinGW



MinGWでクロスコンパイルしようとして壁にぶつかったので翻訳

エラーメッセージは
warning: '~~~' redeclared without dllimport attribute

c++ - Importing inline functions in MinGW - Stack Overflow

質問者

I'm using a shared library that defines inline functions in its header.
Here is a reduced test case, as seen by the compilation unit linking to the library 
(for the version seen by the library, just replace dllimport by dllexport).

(私はヘッダーファイルでインライン関数を定義した動的リンクライブラリを使用しています。以下に削除されたテストケースがあります、見たところライブラリにリンクされる一つのコンパイル単位と思われます。
(ライブラリのバージョンを比べてみると、単にdllimportをdllexportに置き換えただけ))

class __declspec(dllimport) MyClass {
public:
    int myFunc2();
    int myFunc1();
};

inline int MyClass::myFunc2(void) {
    return myFunc1();
}

inline int MyClass::myFunc1(void) {
    return 0;
}
Compiling this gives the warning:

(これをコンパイルすると次のような警告が出力されます)

warning: 'int MyClass::myFunc1()' redeclared without dllimport attribute after being 
referenced with dll linkage [enabled by default]
Please note that the order in which the functions are defined is important, 
as putting the definition of myFunc1 before the definition of myFunc2 results in no warnings.

Note also that this code compiles without warnings under Visual C++. 
These warnings are specific at least to MinGW, maybe to GCC in general. 
Edit: it came to my mind that I may have to verify if the warning is not 
inhibited by one of the flags set by the project.

My questions are then:

・ Why this behavior ?
・ Declaring myFunc1 as inline inside the class declaration fixes the problem. 
Why is that ? It's also against the recommended way of doing things.
・ Is there another (better ?) way to fix this problem ?

(その関数が定義されている順番が重要だという事を覚えておいてください、myFunc2の定義の前にmyFunc1を定義すると、警告は出力されません。

また、このコードはVisual C++では警告なしにコンパイルできます。これらの警告は少なくともMinGWに特有のもので、同様にしてGCCにも同じことが起こるかもしれません。
追記:***

というわけで私の質問は以下の通り:

・どうしてこのような動作をするのか?
・myFunc1をクラス定義のインライン関数として宣言することで、問題は修正される。それはなぜか?そしてこれは勧められるべき方法ではない。
・この問題を修正するために、他に(より良い?)方法はないか)

回答者

The problem comes about because of some magic in the way that dllimport works 
which normally means you only need it on the first declaration.

Basically, when you declare a function as dllimport and then later redeclare 
the function with an identical declaration except for the dllimport, 
the second declaration implicitly gets the dllimport. If the redeclaration is 
NOT identical, it doesn't get the implicit dllimport.

So what happens here is you first declare the function as dllimport/non-inline, 
and then declare it as non-dllimport/inline. Adding an inline to 
the first declaration fixes the problem, as the second then becomes implicitly dllimport. 
Alternately, adding a __declspec(dllimport) to the second declaration should fix the problem.

Note that reordering the definitions gets rid of the warning, 
since the warning is about using it before redeclaring it. With a reorder, 
you're no longer using it before the redeclaration, so you get no warning, 
though it will be using the non-dllimport version 
(ie, it will never use the version of the function from the dll).

Note also, using inline dllimport is dangerous. 
Any program built against the dll might use 
the inline function in some places and the non-inline function (from the dll) in others. 
Even if the two functions are identical NOW, a future version of the dll might change and 
have a different implementation. 
At which point the old program might start misbehaving if run with the new version of the dll.

(その問題はdllimportが普通は最初の宣言のみで動作するという、ある種の"黒魔術"に起因している。

基本的に、関数をdllimportで宣言した後、dllimportを除いて同一の宣言をもってその関数を再定義するとき、2回目の宣言は暗黙的にdllimportを得る。もし再定義が同一で"なかった"場合、それは暗黙的にdllimportされない。

だから、ここで起きていることは:まず関数をdllimport/non-inlineで宣言、そしてnon-dllimport/inlineで宣言している。最初の宣言にinlineをつけることによって問題が解決するのは、それによって2つ目の関数が暗黙的にdllimportになるからです。代わりに2つ目の関数に「__declspec(dllimport)」をつけても問題は解決するはずです。

クラス定義の並べ替えが警告を除去するということに言及しますと、再定義をする前にそれを使っているからです。関数の並べ替えをすることで、再定義前にそれを使うことはできなくなります。だから警告がなくなるのですが、non-dllimportを使うことになります。(DLLからその関数は使えなくなるでしょう。)

もうひとつ、インライン関数をdllimportで使うことの危険さについて言及します。
そのDLLをリンクしてビルドされたあらゆるプログラムが、そのインライン関数/非インライン関数をどこかしらから使用するかもしれません。
今やその2つの関数は同一であるにも関わらず、その将来のDLLは変更され、異なる実装をもつことになるでしょう。
この時点で、新しいバージョンのDLLを使用して動く古いプログラムは誤動作することになるでしょう。

つまり…どういうことだってばよ?

幻術だ

全然知らんかったけど、dllimport, dllexportはWindows特有のものらしい。

参考1:ダイナミック リンク ライブラリ(DLL)の基礎知識
参考2:VC++DLL作成補足(Hishidama's VC++Memo "DLL")
(hishidamaさんのサイトはよく見るけど、恐ろしくまとまってるな…)


Linuxerであり、クロスプラットフォームを目指す私の立場からすると、DLLの形式は別にどっちゃでもいい。要は同じソースからLinux/Windows向けそれぞれの動的リンクライブラリができて欲しいだけだ。

・dllimportを使っているからこの警告及び問題が起きている
・dllimportをdllexportに変更すれば問題解決?

…要調査

追記:
MinGW + libtoolを使ってソースコードを単にDLLにする場合、dllimport/dllexportはいらない