2013-11-22 45 views
3

我將此ProcessStasts.h文件包含在其他兩個.h文件中。編譯包含結構的h文件時出現鏈接錯誤

#pragma once 

#include <mpi.h> 
#include <cstddef> 

struct ProcessStats 
{ 
    int rank, 
    itLeft, 
    crtIt, 
    processFlag; 
    float speed; 
}; 

MPI_Datatype MPI_Cust_ProcessStats_create() 
{ 
    // set data to create new MPI data type 
    MPI_Datatype MPI_Cust_ProcessStats; 
    MPI_Datatype dataTypes[5] = {MPI_INT, MPI_INT, MPI_INT, MPI_INT, MPI_FLOAT}; 
    int blockLengths[5] = {1, 1, 1, 1, 1}; 
    MPI_Aint offsets[5]; 

    offsets[0] = (MPI_Aint) offsetof(ProcessStats, rank); 
    offsets[1] = (MPI_Aint) offsetof(ProcessStats, itLeft); 
    offsets[2] = (MPI_Aint) offsetof(ProcessStats, crtIt); 
    offsets[3] = (MPI_Aint) offsetof(ProcessStats, processFlag); 
    offsets[4] = (MPI_Aint) offsetof(ProcessStats, speed); 

    // create new MPI type based on data from above 
    MPI_Type_create_struct(5, blockLengths, offsets, dataTypes, &MPI_Cust_ProcessStats); 
    MPI_Type_commit(&MPI_Cust_ProcessStats); 

    return MPI_Cust_ProcessStats; 
} 

當我嘗試編譯時,出現此錯誤:error LNK2005: MPI_Cust_ProcessStats_create(void) already defined。如果我對#include "ProcessStasts.h"指令和使用ProcessStats結構的行從其中一個文件發表評論,它會正確編譯。我甚至嘗試評論所有依賴於ProcessStats的行,只留下#include "ProcessStasts.h"語句,我得到這個lnk錯誤。哪裏不對?

+0

當'struct'中有5個字段時,爲什麼MPI結構數據類型構造函數'3'的第一個參數? –

+0

由於出錯,使用更多變量更改結構...與DataType數組[6]相同。謝謝,這讓我看到了一些我以後會發現的bug。 – codiac

回答

4

你可以這樣寫: 首先是ProcessStasts.h

#pragma once 

#include <mpi.h> 
#include <cstddef> 

struct ProcessStats 
{ 
    int rank, 
    itLeft, 
    crtIt, 
    processFlag; 
    float speed; 
}; 

MPI_Datatype MPI_Cust_ProcessStats_create(); 

則是ProcessStasts.c

#include "ProcessStats.h" 
MPI_Datatype MPI_Cust_ProcessStats_create() 
{ 
    // set data to create new MPI data type 
    MPI_Datatype MPI_Cust_ProcessStats; 
    MPI_Datatype dataTypes[6] = {MPI_INT, MPI_INT, MPI_INT, MPI_INT, MPI_FLOAT}; 
    int blockLengths[5] = {1, 1, 1, 1, 1}; 
    MPI_Aint offsets[5]; 

    offsets[0] = (MPI_Aint) offsetof(ProcessStats, rank); 
    offsets[1] = (MPI_Aint) offsetof(ProcessStats, itLeft); 
    offsets[2] = (MPI_Aint) offsetof(ProcessStats, crtIt); 
    offsets[3] = (MPI_Aint) offsetof(ProcessStats, processFlag); 
    offsets[4] = (MPI_Aint) offsetof(ProcessStats, speed); 

    // create new MPI type based on data from above 
    MPI_Type_create_struct(3, blockLengths, offsets, dataTypes, &MPI_Cust_ProcessStats); 
    MPI_Type_commit(&MPI_Cust_ProcessStats); 

    return MPI_Cust_ProcessStats; 
} 

然後你可以包括ProcessStasts.h只要你想多次。作爲建議,不要在頭文件中定義函數。

+0

是的,這解決了問題。不過我不明白爲什麼,因爲'#pragma once'有 – codiac

+2

#pragma在編譯一個源文件時曾經有一次幫助。但是對於不同的源文件,它在編譯時也會執行include操作。 – yanchong

2

#pragma once指示預處理器不要包含頭文件兩次。這主要是用來防止遞歸夾雜物和多個間接夾雜物,例如:

#include <a.h> // a.h already includes b.h 
#include <b.h> 

沒有#pragma onceb.h之初,其內容將得到包含了兩次,並可能導致一些符號的重新定義。

你的情況會發生什麼是完全不同的事情。默認C和C++中的函數具有外部鏈接。這意味着如果你有在文件bar.c中定義的函數foo(),然後你編譯bar.c到目標文件bar.o,那麼目標文件將導出一個名字爲foo的全局符號(實際上C++將修飾名稱以支持重載),這符號可以從其他目標文件訪問(引用)。現在,如果文件baz.c包含另一個函數foo()(在C++具有相同簽名的情況下)的定義,則目標文件baz.o也會導出名稱爲foo的全局符號。當目標文件鏈接在一起以生成可執行文件時,鏈接程序會嘗試將每個符號解析爲唯一的內存地址。但現在有一個問題:有兩個符號foo,它們都有不同的地址。鏈接器(通常)不是通靈者,所以它只是給你一個關於符號重定義的錯誤消息並終止。

C和C++都提供了一種機制來控制函數的鏈接。如果添加關鍵字static,則函數符號不再是全局的,並且只對共享相同編譯單元的代碼可見。這些功能具有靜態鏈接。這就是爲什麼在頭文件中定義的函數幾乎總是與static關鍵字:

#pragma once 

#include <mpi.h> 
#include <cstddef> 

struct ProcessStats 
{ 
    int rank, 
    itLeft, 
    crtIt, 
    processFlag; 
    float speed; 
}; 

static MPI_Datatype MPI_Cust_ProcessStats_create() 
{ 
    ... 
} 

現在MPI_Cust_ProcessStats_create()只會是包含頭文件的源文件中可見。

不請自來的建議:MPI_前綴是爲MPI API調用保留的。將它用於用戶功能是一種糟糕的編程習慣,因爲有些工具依賴於只有MPI調用以MPI_開頭並且可能會感到困惑的事實。

+0

你將如何處理這個問題,將函數移動到.cpp文件或添加靜態,以及爲什麼(一種模式與另一種模式如何獲益)? – codiac

+0

這是一個偏好問題。如果你把它放在一個單獨的源文件中,它只編譯一次。如果你把它放在標題中,它會被編譯很多次。如果將它保存在頭文件中並且必須對函數的代碼進行更改,則必須重新編譯包含頭文件的每個文件。如果它在一個單獨的文件中,只有它必須重新編譯。 –

+0

有些人更喜歡在頭文件中保留小函數,因爲這些函數可以被優化編譯器內聯。如果函數是在單獨的翻譯單元中,那麼它變得更難(但在諸如程序間優化之類的事情中不是不可能的)。 –

相關問題