2010-02-24 3 views
1

Можете ли вы вызвать функцию, которая не имеет тела, но объявлена ​​в заголовке в коде? Пожалуйста, прочтите ниже мою ситуацию.можно объявить функции без тела

Я изучаю C++, поэтому, если моя терминология выключена, тогда вы знаете, почему. Любой, как я читаю эту книгу под названием «Advanced 2D Game Development», поэтому, если кто-то читает книгу, возможно, они могут мне помочь. В C++ он установил 4 Экстерн функции Advanced2D.h

extern bool game_preload(); 
extern bool game_init(HWND); 
extern void game_update(); 
extern void game_end(); 

Позже вниз линии он называет их несколько раз, это находится в одном классе, но никогда не дает им тело. То, что он в конечном итоге пытается сделать со всем кодом, заключается в его компиляции в файл lib, поэтому другие проекты могут включать его и фактически использовать эти 4 метода.

Он хотел, чтобы перейти к решениям свойств/Общие/Output Directory и добавить в обоих Release и Debug конфигурации

$(ProjectDir)..\lib\Advance2D.lib // It didn't work. Still added the libs at default location 

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

#include <iostream> 
#include "..\Engine\Advanced2D.h" 
bool game_preload() 
{ 
    //display engine version in a message box 
    g_engine->message(g_engine->getVersionText(), "TEST ENGINE"); 
    //return fail to terminate the engine 
    return false; 
} 

bool game_init(HWND hwnd) { return 0;} 
void game_update() {} 
void game_end() {} 

Единственная проблема сейчас в том, что я получаю ошибку компоновщика

1>winmain.obj : error LNK2019: unresolved external symbol "bool __cdecl game_preload(void)" ([email protected]@YA_NXZ) referenced in function [email protected] 
1>c:\Engine\msvc8\Advance2D\Advance2D\..\lib\Advance2D.lib\Advance2D.exe : fatal error LNK1120: 1 unresolved externals 

Если я не закомментировать эти методы используются в первом проекте, то этот проект никогда не будет скомпилирован?

Парень настаивает на том, что я не должен получать ошибки компоновщика во время его компиляции. И я цитирую следующее

Предполагая, что вы ввели код в указанных файлов без каких-либо ошибок, вы должны быть в состоянии скомпилировать проект двигателя. Там не должно быть зависимостей для двигателя , потому что компилятор принимает , что вы предоставите необходимые библиотеки во время соединения (при создании исполняемого файла с использованием библиотеки движка). Это довольно сложная проблема, которую мы рассмотрим в следующем нескольких главах, поскольку мы улучшаем двигатель с новыми модулями и функциональностью . Вы не должны видеть ошибки компоновщика, только ошибки компилятора, если вы внесли ошибку при вводе в код.

следующее advanced2D заголовок

// Advanced2D Engine 
// Main header file 
#ifndef _ADVANCED2D_H 
#define _ADVANCED2D_H 1 
#include <iostream> 
#include <windows.h> 
#include <d3d9.h> 
#include <d3dx9.h> 
#include <dxerr.h> 
#include "Timer.h" 
#define VERSION_MAJOR 1 
#define VERSION_MINOR 0 
#define REVISION 0 

#pragma comment(lib,"d3d9.lib") 
#pragma comment(lib,"d3dx9.lib") 
#pragma comment(lib, "winmm.lib") 

//external variables and functions 
extern bool gameover; 
extern bool game_preload(); 
extern bool game_init(HWND); 
extern void game_update(); 
extern void game_end(); 

namespace Advanced2D 
{ 
    class Engine { 
     private: 
      int p_versionMajor, p_versionMinor, p_revision; 
      HWND p_windowHandle; 
      LPDIRECT3D9 p_d3d; 
      LPDIRECT3DDEVICE9 p_device; 
      LPDIRECT3DSURFACE9 p_backbuffer; 
      LPD3DXSPRITE p_sprite_handler; 
      std::string p_apptitle; 
      bool p_fullscreen; 
      int p_screenwidth; 
      int p_screenheight; 
      int p_colordepth; 
      bool p_pauseMode; 
      D3DCOLOR p_ambientColor; 
      bool p_maximizeProcessor; 
      Timer p_coreTimer; 
      long p_frameCount_core; 
      long p_frameRate_core; 
      Timer p_realTimer; 
      long p_frameCount_real; 
      long p_frameRate_real; 
     public: 
      Engine(); 
      virtual ~Engine(); 
      int Init(int width, int height, int colordepth, bool fullscreen); 
      void Close(); 
      void Update(); 
      void message(std::string message, std::string title = "ADVANCED 2D"); 
      void fatalerror(std::string message, std::string title = "FATAL ERROR"); 
      void Shutdown(); 
      void ClearScene(D3DCOLOR color); 
      void SetDefaultMaterial(); 
      void SetAmbient(D3DCOLOR colorvalue); 
      int RenderStart(); 
      int RenderStop(); 
      int Release(); 
      //accessor/mutator functions expose the private variables 
      bool isPaused() { return this->p_pauseMode; } 
      void setPaused(bool value) { this->p_pauseMode = value; } 
      LPDIRECT3DDEVICE9 getDevice() { return this->p_device; } 
      LPDIRECT3DSURFACE9 getBackBuffer() { return this->p_backbuffer; } 
      LPD3DXSPRITE getSpriteHandler() { return this->p_sprite_handler; } 
      void setWindowHandle(HWND hwnd) { this->p_windowHandle = hwnd; } 
      HWND getWindowHandle() { return this->p_windowHandle; } 
      std::string getAppTitle() { return this->p_apptitle; } 
      void setAppTitle(std::string value) { this->p_apptitle = value; } 
      int getVersionMajor() { return this->p_versionMajor; } 
      int getVersionMinor() { return this->p_versionMinor; } 
      int getRevision() { return this->p_revision; } 
      std::string getVersionText(); 
      long getFrameRate_core() { return this->p_frameRate_core; }; 
      long getFrameRate_real() { return this->p_frameRate_real; }; 
      int getScreenWidth() { return this->p_screenwidth; } 
      void setScreenWidth(int value) { this->p_screenwidth = value; } 
      int getScreenHeight() { return this->p_screenheight; } 
      void setScreenHeight(int value) { this->p_screenheight = value; } 
      int getColorDepth() { return this->p_colordepth; } 
      void setColorDepth(int value) { this->p_colordepth = value; } 
      bool getFullscreen() { return this->p_fullscreen; } 
      void setFullscreen(bool value) { this->p_fullscreen = value; } 
      bool getMaximizeProcessor() { return this->p_maximizeProcessor; } 
      void setMaximizeProcessor(bool value) { this->p_maximizeProcessor = value;} 
    }; //class 
}; //namespace 
//define the global engine object (visible everywhere!) 
extern Advanced2D::Engine *g_engine; 
#endif 

Advanced2d класс

// Advanced2D Engine 
// Main source code file 
//includes 
#include "Advanced2D.h" 
#include <cstdlib> 
#include <ctime> 
#include <string> 
#include <sstream> 
#include <list> 
#include "winmain.h" 

namespace Advanced2D 
{ 
    Engine::Engine() 
    { 
     srand((unsigned int)time(NULL)); 
     p_maximizeProcessor = false; 
     p_frameCount_core = 0; 
     p_frameRate_core = 0; 
     p_frameCount_real = 0; 
     p_frameRate_real = 0; 
     p_ambientColor = D3DCOLOR_RGBA(255,255,255, 0); 
     p_windowHandle = 0; 
     p_pauseMode = false; 
     p_versionMajor = VERSION_MAJOR; 
     p_versionMinor = VERSION_MINOR; 
     p_revision = REVISION; 
     //set default values 
     this->setAppTitle("Advanced2D"); 
     this->setScreenWidth(640); 
     this->setScreenHeight(480); 
     this->setColorDepth(32); 
     this->setFullscreen(false); 
     //window handle must be set later on for DirectX! 
     this->setWindowHandle(0); 
    } 

    Engine::~Engine() 
    { 
     if (this->p_device) this->p_device->Release(); 
     if (this->p_d3d) this->p_d3d->Release(); 
    } 

    std::string Engine::getVersionText() 
    { 
     std::ostringstream s; 
     s << "Advanced2D Engine v" << p_versionMajor << "." << p_versionMinor 
     << "." << p_revision; 
     return s.str(); 
    } 

    void Engine::message(std::string message, std::string title) 
    { 
     MessageBox(0, message.c_str(), title.c_str(), 0); 
    } 

    void Engine::fatalerror(std::string message, std::string title) 
    { 
     this->message(message,title); 
     Shutdown(); 
    } 

    int Engine::Init(int width, int height, int colordepth, bool fullscreen) 
    { 
     //initialize Direct3D 

     this->p_d3d = Direct3DCreate9(D3D_SDK_VERSION); 

     if (this->p_d3d == NULL) { 
      return 0; 
     } 

     //get system desktop color depth 
     D3DDISPLAYMODE dm; 
     this->p_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dm); 

     //set configuration options for Direct3D 
     D3DPRESENT_PARAMETERS d3dpp; 
     ZeroMemory(&d3dpp, sizeof(d3dpp)); 
     d3dpp.Windowed = (!fullscreen); 
     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; 
     d3dpp.EnableAutoDepthStencil = TRUE; 
     d3dpp.AutoDepthStencilFormat = D3DFMT_D16; 
     d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; 
     d3dpp.BackBufferFormat = dm.Format; 
     d3dpp.BackBufferCount = 1; 
     d3dpp.BackBufferWidth = width; 
     d3dpp.BackBufferHeight = height; 
     d3dpp.hDeviceWindow = p_windowHandle; 

     //create Direct3D device 
     this->p_d3d->CreateDevice(
     D3DADAPTER_DEFAULT, 
     D3DDEVTYPE_HAL, 
     this->p_windowHandle, 
     D3DCREATE_HARDWARE_VERTEXPROCESSING, 
     &d3dpp, 
     &this->p_device); 
     if (this->p_device == NULL) return 0; 

     //clear the backbuffer to black 
     this->ClearScene(D3DCOLOR_XRGB(0,0,0)); 

     //create pointer to the back buffer 
     this->p_device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &this->p_backbuffer); 

     //use ambient lighting and z-buffering 
     this->p_device->SetRenderState(D3DRS_ZENABLE, TRUE); 
     this->p_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); 
     this->SetAmbient(this->p_ambientColor); 

     //initialize 2D renderer 
     HRESULT result = D3DXCreateSprite(this->p_device, &this->p_sprite_handler); 
     if (result != D3D_OK) return 0; 
     //call game initialization extern function 
     //if (!game_init(this->getWindowHandle())) return 0; 
     //set a default material 
     SetDefaultMaterial(); 
     return 1; 
    } 


    void Engine::SetDefaultMaterial() 
    { 
     D3DMATERIAL9 mat; 
     memset(&mat, 0, sizeof(mat)); 
     mat.Diffuse.r = 1.0f; 
     mat.Diffuse.g = 1.0f; 
     mat.Diffuse.b = 1.0f; 
     mat.Diffuse.a = 1.0f; 
     p_device->SetMaterial(&mat); 
    } 

    void Engine::ClearScene(D3DCOLOR color) 
    { 
     this->p_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 
     color, 1.0f, 0); 
    } 

    void Engine::SetAmbient(D3DCOLOR colorvalue) 
    { 
     this->p_ambientColor = colorvalue; 
     this->p_device->SetRenderState(D3DRS_AMBIENT, this->p_ambientColor); 
    } 
    int Engine::RenderStart() 
    { 
     if (!this->p_device) return 0; 
     if (this->p_device->BeginScene() != D3D_OK) return 0; 
     return 1; 
    } 
    int Engine::RenderStop() 
    { 
     if (!this->p_device) return 0; 
     if (this->p_device->EndScene() != D3D_OK) return 0; 
     if (p_device->Present(NULL, NULL, NULL, NULL) != D3D_OK) return 0; 
     return 1; 
    } 

    void Engine::Shutdown() 
    { 
     gameover = true; 
    } 

    void Engine::Update() 
    { 
     static Timer timedUpdate; 
     //calculate core framerate 
     p_frameCount_core++; 
     if (p_coreTimer.stopwatch(999)) { 
     p_frameRate_core = p_frameCount_core; 
     p_frameCount_core = 0; 
     } 
     //fast update with no timing 
     game_update(); 

     //update with 60fps timing 
     if (!timedUpdate.stopwatch(14)) { 
      if (!this->getMaximizeProcessor()) 
      { 
       Sleep(1); 
      } 
     } 
     else { 
      //calculate real framerate 
      p_frameCount_real++; 

      if (p_realTimer.stopwatch(999)) { 
       p_frameRate_real = p_frameCount_real; 
       p_frameCount_real = 0; 
      } 
      //begin rendering 
      this->RenderStart(); 
      //done rendering 
      this->RenderStop(); 
     } 
    } 
    void Engine::Close() 
    { 
     game_end(); 
    } 
} //namespace 

А вот WinMain

#include <sstream> 
#include "winmain.h" 
#include "Advanced2D.h" 
//macro to read the key states 
#define KEY_DOWN(vk) ((GetAsyncKeyState(vk) & 0x8000)?1:0) 
HINSTANCE g_hInstance; 
HWND g_hWnd; 
int g_nCmdShow; 
//declare global engine object 
Advanced2D::Engine *g_engine; 
bool gameover; 


//window event callback function 
LRESULT WINAPI WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{ 
    switch(msg) 
    { 
    case WM_QUIT: 
    case WM_CLOSE: 
    case WM_DESTROY: 
    gameover = true; 
    break; 
    } 
    return DefWindowProc(hWnd, msg, wParam, lParam); 
} 
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int 
nCmdShow) 
{ 
    MSG msg; 
    srand((unsigned int)time(NULL)); 
    g_hInstance = hInstance; 
    g_nCmdShow = nCmdShow; 
    DWORD dwStyle, dwExStyle; 
    RECT windowRect; 
    /** 
    * Create engine object first! 
    **/ 
    g_engine = new Advanced2D::Engine(); 
    //let main program have a crack at things before window is created 
    if (!game_preload()) { 
     MessageBox(g_hWnd, "Error in game preload!", "Error", MB_OK); 
     return 0; 
    } 
    //get window caption string from engine 
    char title[255]; 
    sprintf_s(title, "%s", g_engine->getAppTitle().c_str()); 
    //set window dimensions 
    windowRect.left = (long)0; 
    windowRect.right = (long)g_engine->getScreenWidth(); 
    windowRect.top = (long)0; 
    windowRect.bottom = (long)g_engine->getScreenHeight(); 
    //create the window class structure 
    WNDCLASSEX wc; 
    wc.cbSize = sizeof(WNDCLASSEX); 
    //fill the struct with info 
    wc.style = CS_HREDRAW | CS_VREDRAW; 
    wc.lpfnWndProc = (WNDPROC)WinProc; 
    wc.cbClsExtra = 0; 
    wc.cbWndExtra = 0; 
    wc.hInstance = hInstance; 
    wc.hIcon = NULL; 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wc.hbrBackground = NULL; 
    wc.lpszMenuName = NULL; 
    wc.lpszClassName = title; 
    wc.hIconSm = NULL; 
    //set up the window with the class info 
    RegisterClassEx(&wc); 
    //set up the screen in windowed or fullscreen mode? 
    if (g_engine->getFullscreen()) 
    { 
     DEVMODE dm; 
     memset(&dm, 0, sizeof(dm)); 
     dm.dmSize = sizeof(dm); 
     dm.dmPelsWidth = g_engine->getScreenWidth(); 
     dm.dmPelsHeight = g_engine->getScreenHeight(); 
     dm.dmBitsPerPel = g_engine->getColorDepth(); 
     dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; 
     if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { 
     MessageBox(NULL, "Display mode failed", NULL, MB_OK); 
     g_engine->setFullscreen(false); 
     } 
     dwStyle = WS_POPUP; 
     dwExStyle = WS_EX_APPWINDOW; 
     ShowCursor(FALSE); 
     } 
     else { 
     dwStyle = WS_OVERLAPPEDWINDOW; 
     dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; 
     } 
     //adjust window to true requested size 
     AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); 
     //create the program window 
     g_hWnd = CreateWindowEx(0, 
     title, //window class 
     title, //title bar 
     dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 
     0, 0, //x,y coordinate 
     windowRect.right - windowRect.left, //width of the window 
     windowRect.bottom - windowRect.top, //height of the window 
     0, //parent window 
     0, //menu 
     g_hInstance, //application instance 
     0); //window parameters 
     //was there an error creating the window? 
     if (!g_hWnd) { 
     MessageBox(g_hWnd, "Error creating program window!", "Error", MB_OK); 
     return 0; 
    } 
    //display the window 
    ShowWindow(g_hWnd, g_nCmdShow); 
    UpdateWindow(g_hWnd); 
    //initialize the engine 
    g_engine->setWindowHandle(g_hWnd); 
    if (!g_engine->Init(g_engine->getScreenWidth(), g_engine->getScreenHeight(), 
    g_engine->getColorDepth(), g_engine->getFullscreen())) { 
    MessageBox(g_hWnd, "Error initializing the engine", "Error", MB_OK); 
    return 0; 
    } 
    // main message loop 
    gameover = false; 
    while (!gameover) 
    { 
     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
     { 
      TranslateMessage(&msg); 
      DispatchMessage(&msg); 
     } 
     g_engine->Update(); 
    } 

    if (g_engine->getFullscreen()) { 
     ShowCursor(TRUE); 
    } 
    g_engine->Close(); 
    delete g_engine; 
    return 1; 
} 
+0

По терминологии: ваше использование 'declare' является правильным. Термин, который вам не хватает, это «define». Определение функции или метода обеспечивает реализацию (тело в фигурных скобках). –

ответ

1

Ключевое слово extern указывает, что функция будет реализована в объекте компиляции, внешнем по отношению к тому, в котором вы находитесь - обычно через lib или другой объект. Если вы должны объявить переменную в глобальной области видимости в одном файле cpp (за пределами любого тела функции), вы можете ссылаться только на нее, используя внешнее объявление переменной в другом файле cpp, чтобы компилятор не знал, что функция/переменная будет определена но знаю, что он существует.

Что здесь происходит от звуков вещей, так это то, что объект, с которым вы связываетесь, на самом деле не связан - это должно быть. Вышеупомянутый ответ подскажет вам, как - я недостаточно быстро! Из вашего вопроса кажется, что у вас есть источник этого второго проекта и, возможно, нужно его самостоятельно построить - я не знаю, конечно, но если .lib не существует, вам нужно будет.

+0

Взгляните на класс advanced2d. Если вы посмотрите на дно, вы увидите game_update(); и game_end(); они представляют собой 2 метода externs, которые объявляются в advanced2d.h. Они называются, но у них нет тел. Поэтому для того, чтобы файл lib существовал, я должен скомпилировать его как есть, и тогда я получаю сообщение об ошибке. Я мог бы прокомментировать это, и тогда это сработает. но тогда я бы включил библиотеку, которая пропускает эти строки кода. – numerical25

+0

Я понимаю, что такое ключевое слово extern. Я не понимаю, почему это не скомпилируется, когда Артур настаивает на этом. Я сузил проблему до этих строк. game_end(); и game_update(); 2 функции, вызываемые без тела, и единственное, что заставляет все приложение выкидывать ошибку. – numerical25

1

Из вашего описания, я полагаю, вы используете какую-то версию из MS Visual C++. Убедитесь, что у вас есть Advance2D.lib в поле ProjectProperties -> Linker-> Input -> Additional Dependencies. Также добавьте путь к этой библиотеке в ProjectProperties -> Linker-> General -> Дополнительные библиотечные каталоги.Не требуется, чтобы вы изменили выходной путь по умолчанию проекта двигателя. BTW, если вы новичок на C++, лучше прочитайте что-нибудь вроде или Thinking In C++, прежде чем погрузиться в «продвинутый» материал.

+0

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

+0

см. Мой ответ на девять фишек. – numerical25

1

Ваша ошибка - ошибка компоновщика, а не ошибка компилятора. Ваш код компилируется отлично, но компоновщик не может сгенерировать исполняемый файл, пока все ссылочные функции не будут иметь тело. Линкер либо не знает о библиотеке, которая содержит тела функции, либо что библиотека не содержит их.

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

Во втором случае вы должны проверить экспортированные символы этой библиотеки. Если библиотека была скомпилирована как C++, имена функций могли быть искалечены для типов (необходимо указать как экспортированные в библиотеке для предотвращения этого), либо они могут иметь другую подпись, и вам нужно изменить свои внешние выражения. У VC есть инструмент для отображения информации о библиотеках, я думаю, что он назывался dumpbin.

+0

Вот почему я думаю, что парень, который написал книгу, неправильно редактировался. Потому что он хочет, чтобы я скомпилировал некоторый код, только чтобы превратиться в файл lib. Но проблема в том, что я не могу скомпилировать код из-за отсутствия определений для методов extern. Он хочет, чтобы другие проекты включали эти файлы lib, а затем будут определены методы. Но я не могу дойти до этого, если получаю ошибки – numerical25

0

Просто хотел добавить в случае, если вы все еще застряли в этой проблеме, или если кто-то еще найдет эту тему и захочет получить дополнительные сведения об устранении неполадок при работе через книгу Advanced 2D Game Dev.

На стр.16, где он перебирает установку VS для двигателя, убедитесь, что вы не только изменяете целевое расширение на .lib, но также убедитесь, что для параметра «Тип конфигурации» установлено значение «Статическая библиотека» (.lib), а не Динамическая библиотека (DLL).

Если вы оставите Конфигурационный тип как динамическую библиотеку, вы увидите ошибку LNK2019 для функций extern, потому что компоновщик VC++ ожидает, что эти функции будут иметь определение для создания рабочего файла DLL. Когда вы собираете статическую библиотеку, фактические определения для внешних переменных, функций и т. Д. Не обязательно должны присутствовать, пока вы, наконец, не соедините все вместе в DLL или EXE (как это делается в последующих проектах на протяжении всей книги).

Некоторые ссылки, которые могут быть полезны по этой теме:

Использование EXTERN для указания Linkage: msdn.microsoft.com/en-us + /library/0603949d.aspx

Краткое руководство: создание и использование Статическая библиотека: msdn.microsoft.com/en-us + /library/ms235627%28VS.80%29.aspx

Microsoft Visual C++ статические и динамические библиотеки: http://www.codeproject.com/KB/cpp/libraries1.aspx

Упование вы считаете, что эта информация полезна!

* примечание: извините за сумасшедшие ссылки msdn выше, по-видимому, я не могу опубликовать более одной ссылки, пока у меня не будет еще больше StackOverflow.