2011-01-15 41 views
3

在過去的幾天裏,我一直在試圖弄清楚如何鏈接我一直在研究的CLI遊戲項目的文件。有兩個項目,客戶端和服務器代碼。(C++)與名稱空間的鏈接會導致重複的符號錯誤

客戶端需要兩個我創建的庫。第一個是通用遊戲板。這被分成GameEngine.h和GameEngine.cpp。頭文件看起來像這樣

namespace gfdGaming { 

// struct sqr_size { 
//  Index x; 
//  Index y; 
// }; 
    typedef struct { Index x, y; } sqr_size; 
    const sqr_size sPos = {1, 1}; 
    sqr_size sqr(Index x, Index y); 
    sqr_size ePos; 
    class board 
    { 
    // Prototypes/declarations for the class 
    } 
} 

而CPP文件只是給一切內容

#include "GameEngine.h" 

type gfdGaming::board::functions 

客戶端還具有特定的遊戲代碼(在這種情況下,井字遊戲)分成聲明和定義(TTT.h,Client.cpp)。 TTT.h基本上是

#include "GameEngine.h" 

#define TTTtar "localhost" 
#define TTTport 2886 

using namespace gfdGaming; 
void* turnHandler(void*); 
namespace nsTicTacToe 
{ 
    GFDCON gfd; 
    const char X = 'X'; 
    const char O = 'O'; 
    string MPhostname, mySID; 
    board TTTboard; 
    bool PlayerIsX = true, isMyTurn; 
    char Player = X, Player2 = O; 

    int recon(string* datHolder = NULL, bool force = false); 
    void initMP(bool create = false, string hn = TTTtar); 
    void init(); 
    bool isTie(); 
    int turnPlayer(Index loc, char lSym = Player); 
    bool checkWin(char sym = Player); 

    int mainloop(); 

    int mainloopMP(); 

}; // NS 

我做了,因爲有不會在OOP很好地工作一些地方把這個名字空間劃分它,而不是一類的決定,並實施以後,它更容易。

我在過去連接客戶端時遇到了困難,但是這個設置似乎有效。

我的服務器也分成兩個文件Server.h和Server.cpp。

Server.h正好包含:

#include "../TicTacToe/TTT.h" // Server needs a full copy of TicTacToe code 

class TTTserv; 
struct TTTachievement_requirement { 
    Index id; 
    Index loc; 
    bool inUse; 
}; 
struct TTTachievement_t { 
    Index id; 
    bool achieved; 
    bool AND, inSameGame; 
    bool inUse; 
    bool (*lHandler)(TTTserv*); 
    char mustBeSym; 
    int mustBePlayer; 
    string name, description; 
    TTTachievement_requirement steps[safearray(8*8)]; 

}; 

class achievement_core_t : public GfdOogleTech { 
public: // May be shifted to private 
    TTTachievement_t list[safearray(8*8)]; 
public: 
    achievement_core_t(); 

    int insert(string name, string d, bool samegame, bool lAnd, int lSteps[8*8], int mbP=0, char mbS=0); 
}; 


struct TTTplayer_t { 
    Index id; 
    bool inUse; 
    string ip, sessionID; 
    char sym; 
    int desc; 
    TTTachievement_t Ding[8*8]; 
}; 
struct TTTgame_t { 
    TTTplayer_t Player[safearray(2)]; 
    TTTplayer_t Spectator; 
    achievement_core_t achievement_core; 
    Index cTurn, players; 
    port_t roomLoc; 
    bool inGame, Xused, Oused, newEvent; 
}; 


class TTTserv : public gSserver { 
    TTTgame_t Game; 
    TTTplayer_t *cPlayer; 

    port_t conPort; 
public: 
    achievement_core_t *achiev; 
    thread threads[8]; 
    int parseit(string tDat, string tsIP); 
    Index conCount; 
    int parseit(string tDat, int tlUser, TTTplayer_t** retval); 

private: 
    int parseProto(string dat, string sIP); 
    int parseProto(string dat, int lUser); 

    int cycleTurn(); 
    void setup(port_t lPort = 0, bool complete = false); 

public: 
    int newEvent; 
    TTTserv(port_t tlPort = TTTport, bool tcomplete = true); 

    TTTplayer_t* userDC(Index id, Index force = false); 
    int sendToPlayers(string dat, bool asMSG = false); 
    int mainLoop(volatile bool *play); 
}; 



// Other 
void* userHandler(void*); 
void* handleUser(void*); 

而在CPP文件包括我Server.h並提供main()和先前聲明的所有功能的內容。

現在手頭的問題 我在連接我的服務器時遇到問題。更具體地說,我在nsTicTacToe(也可能在gfdGaming中)爲每個變量都得到了一個重複的符號錯誤。因爲我需要井字遊戲功能,我鏈接Client.cpp(無主())建立服務器

ld: duplicate symbol nsTicTacToe::PlayerIsX  in Client.o and Server.o 
collect2: ld returned 1 exit status 
Command /Developer/usr/bin/g++-4.2 failed with exit code 1 

It stops once a problem is encountered, but if PlayerIsX is removed/changed temporarily than another variable causes an error 

本質的時候,我正在尋找如何更好地組織我的代碼,希望能夠修正這些錯誤的任何建議。

免責聲明: 如果我提供過多或過少的信息 - 我提前道歉,因爲這是我第一次發佈

-I使用靜態和extern解決這些問題都試過了,但顯然這些都不是我所需要的

謝謝你的人誰花時間閱讀這一切和應對=)

+0

嘗試使用標頭警衛,你沒有使用任何。 – DumbCoder

+1

我在下面提供了一個答案,但也使用標頭守衛,正如其他評論者指出的那樣。 – lefticus

+0

因爲他有鏈接的問題,頭衛兵可能不是他唯一的問題,雖然使用它們總是比我更好(我認爲?)比不使用。 – stnr

回答

6

你得到錯誤關於重複定義,因爲這是你擁有的一切:每次.cpp文件包括TT T.h,定義了一個全局的bool PlayerIsX(在nsTicTacToe命名空間中,但仍然是全局的)。在這種情況下,它包含Server.cpp和Client.cpp。

解決此問題的一種方法是通過使用extern將定義更改爲聲明,然後在相應的.cpp文件(例如TTT.cpp)中進行實際定義。

在TTT.h:

namespace nsTicTacToe { 
    ... 
    extern bool PlayerIsX; 
    ... 
} 

在TTT.cpp:

#include "TTT.h" 

bool nsTicTacToe::PlayerIsX; 

等的其他定義。

對了,記得有適當的後衛#ifdef S:

#ifndef __TTT_H 
#define __TTT_H 
... header contents 
#endif // __TTT_H 
+0

非常感謝您的快速回復,並感謝您提供所有有用的信息。事實證明,這正是我需要的。現在完美運作。 – Gfdking

+1

@漫畫:沒問題。不過,我也會推薦lefticus關於改進代碼組織的建議(使用struct或class)。 – maxelost

1

你可以把命名空間nsTicTacToe部分到它自己的.cpp文件,單獨編譯和鏈接它 您可能還需要。一個只爲變量聲明extern的頭文件,並將其包含在客戶端和服務器.cpp文件中。

3

你基本上是使用全局變量,這是強烈不是在C++推薦,但有時C.

必要

你可以得到它的extern的工作,但「好」的答案是來包裝你的全局數據某種狀態對象。

struct State 
{ 
    GFDCON gfd; 
    const char X; 
    const char O; 
    string MPhostname, mySID; 
    board TTTboard; 
    bool PlayerIsX, isMyTurn; 
    char Player, Player2; 
}; 

在Main中創建您的狀態對象並將其傳遞給每個需要了解遊戲系統狀態的函數。

長遠來看,這將導致更好的代碼組織。

+0

謝謝。是的,我回想起來,將數據保存在一個對象中會更好。雖然現階段重組我的代碼聽起來不太有吸引力,但我一定會記住這一點=) – Gfdking

2

其實,extern是你所需要的。你可能只是沒有意識到或記住你還必須在cpp文件中定義這樣的變量。

頭:

extern int somevar; 

來源:

int somevar = ?; 

通過把所有的全局變量在頭你讓它們的副本,隨處可見它們包括,這是你的編譯器是什麼嘮叨。

+1

感謝您的迴應!你是對的,事實證明我稍微誤解了如何實現extern。現在修復 – Gfdking