2016-01-25 16 views
6

Przebudowuję niektóre kod C i wykonuję testy jednostkowe na wydzielonych częściach (za pomocą testu Google). Jeden fragment został użyty kilka razy w pętli, więc aby wystawić go na testowanie, podałem go jako funkcję inline w pliku nagłówkowym demo.h, który zawiera także deklaracje niektórych innych funkcji innych niż inline. Uproszczona wersja jest następująca:Dlaczego funkcja inline musi być zadeklarowana jako statyczna, jeśli używa 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_ */ 

Mój kod testowy

#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(); 
} 

Ten kod kompiluje i działa poprawnie pod MinGW g ++ 4.8.1 z -std=gnu++0x.

Oryginalny kod C następnie wykorzystuje tę funkcję. Uproszczona wersja byłyby następujące:

#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); 
    } 
} 

Jednak gdy próbuję skompilować mój kod C przy użyciu MinGW GCC 4.8.1 z -std=c99, pojawia się następujące ostrzeżenie:

ostrzeżenie: „fprintf 'jest statyczna, ale używane w funkcji inline «print_line» który nie jest statyczny [domyślnie włączona]

ja też się kolejny błąd, który może być związany:

niezdefiniowana odniesienia do `print_line”

Zmiana podpis w nagłówku do static inline void print_line ... wydaje się rozwiązać problem. Nie podoba mi się jednak zrozumienie przyczyny problemu. Dlaczego brak testu static nie wpłynął na test C++? A co właściwie oznaczał błąd dotyczący fprintf?

+4

Pierwszy problem, '-std = c99' jest nieprawidłowy, ponieważ ** TO NIE JEST ** [tag: c]. –

+0

@iharob Uważam, że jesteś w błędzie. Kod testowy to C++, ale plik nagłówkowy to C i jest używany w kodzie napisanym w języku C. Myślę, że bardzo jasno wyjaśniłem to w moim pytaniu. – beldaz

+1

Funkcje inline nie są używane, dopóki nie zostaną wywołane gdzieś, wtedy wywołanie zostanie zastąpione przez treść funkcji (tak jak w przypadku makr). więc w kodzie 'print_line' wskazuje się donikąd. – milevyo

Odpowiedz

6

Bez static, pozwalasz kompilatorowi C99 utworzyć zarówno funkcję z zewnętrznym powiązaniem (zdefiniowanym w jednym miejscu), ale także osobny kod wbudowany w każdej jednostce tłumaczeniowej, która zawiera plik. Może używać dowolnej funkcji, którą lubi, chyba że wyraźnie zdecydujesz między static lub extern.

Jednym wymogiem dla takich funkcji może być postrzegane w C99 Draft 6.7.4.3:

definicję inline funkcji z zewnętrznym łącznikiem nie może zawierać definicję modyfikowalnym obiektu ze statycznymi czas przechowywania, i nie zawiera odniesienie do identyfikatora z wewnętrznym powiązaniem.

Ma to sens, ponieważ kompilator chce, aby ta funkcja zachowywała się jednakowo, bez względu na to, jak zdecyduje się ją wdrożyć.

W tym przypadku kompilator narzeka, że ​​twoja niestatyczna funkcja inline wywołuje inną funkcję, tj. static, i nie jest pewna, czy ta inna funkcja (fprintf) nie powoduje mutacji pamięci statycznej.

+1

Dzięki, że konkretnie odpowiada na problem, który obserwowałem, i pomocnie interpretuje to niepokojące ostrzeżenie. – beldaz

4

Przede wszystkim zachowanie się inline, szczególnie w odniesieniu do powiązania symboli, jest różne w C do C++. (A także różni się między ISO C i GNU C).

Możesz przeczytać o wersji C here.

Jeśli spróbujesz umieścić treść funkcji w nagłówku dołączonym zarówno do C, jak i C++ (w ramach tego samego projektu), wówczas otwierasz prawdziwą puszkę robaków. Ta sytuacja nie jest objęta żadnym standardem językowym. Z praktycznego punktu widzenia traktowałbym to jako ODR violation, ponieważ wersja C tej funkcji jest inna niż wersja C++.

Bezpiecznym rozwiązaniem jest włączenie tylko prototypu funkcji do nagłówka i umieszczenie treści funkcji w jednym z plików źródłowych innych niż nagłówek.

+1

Dzięki, połączona odpowiedź SO jest doskonałym zasobem sama w sobie. Umieszczenie tej funkcji w osobnej jednostce kompilacji wydaje się być najlepszym podejściem, a ja zostawiam inicjały dla LTO. – beldaz

Powiązane problemy