2016-01-25 12 views
6

私はいくつかのCコードをリファクタリングし、分解された部品についてユニットテストを行っています(Google Testを使用しています)。 1つのスニペットがループで複数回使用されていたので、テストに公開するために、inlineというファンクションをヘッダファイルdemo.hに組み込みました。これには他の非inline関数の宣言も含まれています。次のように単純化されたバージョンは次のとおりです。なぜfprintfを使用する場合、インライン関数を静的宣言する必要がありますか?

#ifndef DEMO_H_ 
#define DEMO_H_ 
#ifdef __cplusplus 
extern "C" { 
#endif 
inline void print_line(FILE* dest, const double * data, int length) { 
    for (int s = 0; s < length; s++) 
     fprintf(dest, "%lf ", data[s]); 
    fprintf(dest, "\n"); 
} 
#ifdef __cplusplus 
} 
#endif 
#endif /* MK_H_ */ 

私のテストコード

#include "gtest/gtest.h" 
#include "demo.h" 
#include <memory> 
#include <array> 
#include <fstream> 

TEST (demo, print_line) { 
    std::array<double,4> test_data = {0.1, 1.4, -0.05, 3.612}; 

    const char* testfile = "print_line_test.txt"; 
    { 
     auto output_file = std::unique_ptr<FILE, decltype(fclose)*>{ 
      fopen(testfile, "w"), fclose }; 
     print_line(output_file.get(), test_data.data(), test_data.size()); 
    } 

    std::ifstream input(testfile); 
    double dval; 
    for(const auto& v: subsequence_data) { 
     input >> dval; 
     EXPECT_EQ (v, dval); 
    } 
    EXPECT_FALSE (input >> dval) << "No further data"; 
} 

int main(int argc, char **argv) { 
    ::testing::InitGoogleTest(&argc, argv); 
    return RUN_ALL_TESTS(); 
} 

このコードはコンパイルして-std=gnu++0xでのMinGWグラム++ 4.8.1の下に細かい動作します。

元のCコードはこの関数を使用します。単純化されたバージョンは、次のようになります。

#include "demo.h" 

void process_data(const char* fname, double ** lines, int num_lines, int line_length) { 
    FILE* output_file = fopen(fname, "w"); 
    for (int i=0; i<num_lines; ++i) { 
     print_line(output_file, lines[i], line_length); 
    } 
} 

しかし、私は-std=c99とMinGWのGCC 4.8.1を使用して、私のCコードをコンパイルしようとすると、私は次の警告を得る:

警告:「fprintfのをprint_line 『静的ない '静的が、インライン関数で使用される』私はまた、関連している可能性があり、後続のエラーを取得

[デフォルトで有効]:

にヘッダ内の署名を変更する `print_line」

から

未定義の参照は、問題を修正するように見えます。しかし、私は問題の原因を理解していないのが好きではありません。 staticの欠如がC++テストに影響しなかったのはなぜですか?そして、実際にはfprintfに関するエラーはどういう意味ですか?

+4

最初の問題は、 '-std = c99'は** ** [tag:c]ではないからです。 –

+0

@iharob私はあなたがかなり間違っていると信じています。テストコードはC++ですが、ヘッダーファイルはCで、C言語で書かれたコードで使用されています。私はこれを私の質問で非常に明確にしたと思います。 – beldaz

+1

インライン関数は、それらがどこかで呼び出されない限り使用されず、呼び出しは(マクロと同様に)関数本体に置き換えられます。だから、あなたのコードで 'print_line'はどこにもありません。 – milevyo

答えて

6

staticを指定しないと、C99コンパイラは外部リンケージ(1か所で定義されています)の両方の機能を作成できますが、ファイルを含むすべての翻訳単位でインラインコードが分離されます。 staticまたはexternの間で明示的に決定しない限り、好きな機能を使用できます。

ような機能の要件はC99 Draft 6.7.4.3に見ることができるいずれか:

外部結合を持つ関数のインライン定義は、静的記憶寿命変更オブジェクトの定義を含んではならない、との参照を含んではなりません内部リンケージを持つ識別子に変換します。

これは意味があります。これは、コンパイラがこの機能を実装する方法に関係なく、この関数が同じように動作するためです。

したがって、この場合、コンパイラは、非静的インライン関数がstaticという別の関数を呼び出していると不満を持ち、この他の関数(fprintf)が静的記憶域を変更しないことを確認していません。

+1

ありがとうございます。それは具体的に私が観察していた問題に答えるものであり、厄介な警告を役に立ちます。 – beldaz

4

まず、inlineの挙動は、特に関与するシンボルのリンケージに関して、CからC++では異なります。 (また、ISO CとGNU Cの違いもあります)。

Cのバージョンhereについて読むことができます。

(同じプロジェクト内で)CとC++の両方からインクルードされているヘッダーに関数本体を配置しようとすると、実際のワームの缶が開かれています。そのような状況はどちらの言語規格にも該当しません。実際には、私はODR violationとして扱います。なぜなら、Cバージョンの関数はC++バージョンとは異なるからです。

安全なことは、ヘッダに関数プロトタイプのみを含めることで、非ヘッダソースファイルの1つに関数本体を含めることです。

+1

ありがとう、リンクされたSOの回答は、それ自体で優れたリソースです。関数を別のコンパイル単位に入れるのが最善のアプローチのように思えますが、私はLTOのインライン展開を続けます。 – beldaz

関連する問題