(C ++) Связывание с пространствами имен вызывает ошибку дублированного символа - PullRequest
5 голосов
/ 16 января 2011

Последние несколько дней я пытался выяснить, как связать файлы для игрового проекта CLI, над которым я работал. Есть две половины проекта, код клиента и сервера.

Клиенту нужны две библиотеки, которые я создал. Первая - игровая доска общего назначения. Это разделено между 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

У клиента также есть специфичный для игры код (в данном случае TicTacToe), разбитый на объявления и определения (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

Я принял решение поместить это в пространство имен, чтобы сгруппировать его вместо класса, потому что есть некоторые части, которые не будут хорошо работать в ООП, и это будет намного проще реализовать позже.

В прошлом у меня были проблемы с подключением клиента, но эта настройка, похоже, работает.

Мой сервер также разделен на два файла: 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). Поскольку мне нужны функции TicTacToe, я связываю Client.cpp (без main ()) при сборке сервера

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

По сути, я ищу любой совет о том, как лучше организовать мой код, чтобы, надеюсь, исправить эти ошибки.

Отказ от ответственности: -Я заранее извиняюсь, если предоставил слишком много или слишком мало информации, так как я впервые публикую

-Я пытался использовать static и extern для решения этих проблем, но, видимо, это не то, что мне нужно

Спасибо всем, кто нашел время, чтобы прочитать все это и ответить =)

Ответы [ 4 ]

6 голосов
/ 16 января 2011

Вы получаете сообщение об ошибке дубликатов определений, потому что это то, что у вас есть: каждый раз, когда файл .cpp включает TTT.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
3 голосов
/ 16 января 2011

Вы, по сути, используете глобальные переменные, что категорически не рекомендуется в 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 и передайте его каждой функции, которая должна знать состояние игровой системы.

Это приведет кгораздо лучшая организация кода в долгосрочной перспективе.

2 голосов
/ 16 января 2011

На самом деле, extern - это то, что вам нужно.Вы, вероятно, просто не понимаете или не помните, что вам также придется определять такие переменные в файле cpp.

header:

extern int somevar;

source:

int somevar = ?;

Поместив все свои глобальные переменные в заголовок, вы создаете их копии везде, где вы их включаете, и это именно то, о чем суетится ваш компилятор.

1 голос
/ 16 января 2011

Вы можете поместить часть пространства имен nsTicTacToe в свой собственный файл .cpp, скомпилировать его отдельно и связать его. Вам также может понадобиться файл заголовка, который просто объявляет внешние переменные, и включает его в ваш клиент и сервер .cppфайлы.

...