Search code examples
c++visual-studiolinkersingletonstatic-functions

visual studio cannot resolve static functions


Here is the code and the output. win_main.cpp

#include <Windows.h>
#include <tchar.h>

#include "d3d9_object.h"

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

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
WNDCLASSEX createWndClassEx(HINSTANCE);
HWND createWindow(const wchar_t *caption, HINSTANCE);
void exitWithFailure(const wchar_t *message, HINSTANCE);

const wchar_t gszWndClassName[] = L"MainWindowClassName";
WNDCLASSEX gWndClassEx;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, int iCmdShow)
{
    HWND hwnd = NULL;
    MSG msg = { 0 };
    BOOL bReturn;

    WNDCLASSEX wndClassEx = createWndClassEx(hInstance);
    if (!RegisterClassEx(&wndClassEx)) exitWithFailure(L"Cannot create window class", hInstance);

    hwnd = createWindow(L"£d \u018Fditor", hInstance);
    if (!hwnd)
        exitWithFailure(L"Cannot create window", hInstance);
    if (gpd39o->init(hwnd) == false)
        exitWithFailure(L"Cannot initilize Direct3D", hInstance);
    ShowWindow(hwnd, SW_MAXIMIZE);
    UpdateWindow(hwnd);
    if (!gpd39o->clear()) MessageBox(hwnd, L"Cannot clear device to color", L"Error!", MB_OK | MB_ICONERROR);
    while ((bReturn = GetMessage(&msg, hwnd, 0, 0))) {
        if (bReturn == -1) {
            exitWithFailure(L"Message loop error", hInstance);
        }
        else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    gpd39o->deinit();
    UnregisterClass(gszWndClassName, hInstance);
    return EXIT_SUCCESS;
}

void exitWithFailure(const wchar_t *message, HINSTANCE h)
{
    MessageBox(NULL, message, L"Error!", MB_OK | MB_ICONERROR | MB_SYSTEMMODAL);
    gpd39o->deinit();
    UnregisterClass(gszWndClassName, h);
    exit(EXIT_FAILURE);
}

LRESULT CALLBACK WndProc(HWND h, UINT m, WPARAM w, LPARAM l)
{
    if (m == WM_DESTROY) {
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(h, m, w, l);
}

HWND createWindow(const wchar_t *c, HINSTANCE h)
{
    DWORD winStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME;
    return CreateWindowEx(WS_EX_APPWINDOW, gszWndClassName, c, winStyle,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, h, NULL);
}

WNDCLASSEX createWndClassEx(HINSTANCE h)
{
    WNDCLASSEX w = { 0 };
    w.cbSize = sizeof(WNDCLASSEX);
    w.hInstance = h;
    w.style = CS_HREDRAW | CS_VREDRAW;
    w.lpszClassName = (LPCWSTR)gszWndClassName;
    w.lpfnWndProc = WndProc;
    return w;
}

A class with all static members which causes problems.

#ifndef _D3D9_OBJECT_H_
#define _D3D9_OBJECT_H_

#include <d3d9.h>
#include <d3dx9.h>
#include <d3dx9math.h>
#include "vertex_types.h"

/*
 - sm_ - static member
 - d39 - Direct3D9
 - f   - float
*/
class D3D9Object
{
public:
    D3D9Object() { }

    static bool init(HWND);
    static void deinit();
    static bool clear(int color = D3DCOLOR_XRGB(0, 0, 0), float fZDepth = 1.0f);

    static IDirect3D9 * sm_pId39;
    static IDirect3DDevice9 * sm_pd39Device;

    ~D3D9Object() { }
private:
    static HWND sm_hWnd;
    static float sm_fAspectRatio;
    static HRESULT sm_hResult;

    static bool createDevice();
    static void setPresentParams(D3DPRESENT_PARAMETERS &);
    static bool setDefaultRenderState();
};

extern D3D9Object * gpd39o;

#endif

d3d9_object.cpp #include "d3d9_object.h"

IDirect3D9 * D3D9Object::sm_pId39;
IDirect3DDevice9 * D3D9Object::sm_pd39Device;
HRESULT D3D9Object::sm_hResult;
HWND D3D9Object::sm_hWnd;
float D3D9Object::sm_fAspectRatio;

bool D3D9Object::init(HWND h)
{
    if (!h) return false;
    sm_hWnd = h;
    if (!(sm_pId39 = Direct3DCreate9(D3D_SDK_VERSION))) return false;
    if (!createDevice()) return false;
    if (!setDefaultRenderState()) return false;
    return true;
}

void D3D9Object::deinit()
{
    if (sm_pId39) sm_pId39->Release();
    if (sm_pd39Device) sm_pd39Device->Release();
    sm_pId39 = NULL;
    sm_pd39Device = NULL;
    sm_hWnd = NULL;
}

bool D3D9Object::clear(int c, float z)
{
    sm_hResult = sm_pd39Device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, c, z, 0);
    return sm_hResult == D3D_OK;
}

bool D3D9Object::setDefaultRenderState()
{
    sm_hResult = sm_pd39Device->SetRenderState(D3DRS_LIGHTING, FALSE);
    if (sm_hResult != D3D_OK) return false;
    sm_hResult = sm_pd39Device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
    if (sm_hResult != D3D_OK) return false;
    return true;
}

bool D3D9Object::createDevice()
{
    if (sm_pd39Device || !sm_pId39) return false;
    D3DPRESENT_PARAMETERS params = { 0 };
    setPresentParams(params);
    sm_fAspectRatio = (float)params.BackBufferWidth / (float)params.BackBufferHeight;
    sm_hResult = sm_pId39->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, sm_hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING,
        &params, &sm_pd39Device);
    if (sm_hResult != D3D_OK) {
        sm_hResult = sm_pId39->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, sm_hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
            &params, &sm_pd39Device);
        if (sm_hResult != D3D_OK) return false;
    }
    return true;
}

void D3D9Object::setPresentParams(D3DPRESENT_PARAMETERS &p)
{
    ZeroMemory(&p, sizeof(D3DPRESENT_PARAMETERS));
    p.hDeviceWindow = sm_hWnd;
    p.BackBufferCount = 1;
    p.SwapEffect = D3DSWAPEFFECT_FLIP;
    p.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
    p.EnableAutoDepthStencil = TRUE;
    p.AutoDepthStencilFormat = D3DFMT_D16;
    RECT r = { 0 };
    GetClientRect(sm_hWnd, &r);
    p.Windowed = true;
    p.BackBufferWidth = r.right;
    p.BackBufferHeight = r.bottom;
    p.BackBufferFormat = D3DFMT_UNKNOWN;
}

D3D9Object gd39Object;
extern D3D9Object * gpd39o = &gd39Object;

And here is the output

1>------ Build started: Project: 3DEditor, Configuration: Debug Win32 ------
1>  win_main.cpp
1>  d3d9_object.h
1>  d3d9_object.cpp
1>  Generating Code...
1>Debug\d3d9_object.obj : warning LNK4042: object specified more than once; extras ignored
1>win_main.obj : error LNK2019: unresolved external symbol "public: static bool __cdecl D3D9Object::init(struct HWND__ *)" (?init@D3D9Object@@SA_NPAUHWND__@@@Z) referenced in function _WinMain@16
1>win_main.obj : error LNK2019: unresolved external symbol "public: static void __cdecl D3D9Object::deinit(void)" (?deinit@D3D9Object@@SAXXZ) referenced in function "void __cdecl exitWithFailure(wchar_t const *,struct HINSTANCE__ *)" (?exitWithFailure@@YAXPB_WPAUHINSTANCE__@@@Z)
1>win_main.obj : error LNK2019: unresolved external symbol "public: static bool __cdecl D3D9Object::clear(int,float)" (?clear@D3D9Object@@SA_NHM@Z) referenced in function _WinMain@16
1>C:\Users\User\documents\visual studio 2013\Projects\3DEditor\Debug\3DEditor.exe : fatal error LNK1120: 3 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Could you tell me what could cause LNK4042? I don't get where the object is specified multiple times. And I tried to google LNK2019, but all the topics I found were syntax issues. I checked everything I could find, but the code seems to be good (i'd be glad if it wasn't and you'd just point my mistake to me). Sometimes, when I do minor changes to the code the linker succeeds, but after several compilations it fails again. Also, before I added "clear" function it was working, I added this one little function, and it worked only once since then (before I added this function sometimes the linker did fail, but now it sometimes succeeds). I know there are other ways to implement singleton, but I have code similar to this, and it works fine. Thanks


Solution

  • Your build output shows that you somehow managed to force the compiler to actually compile d3d9_object.h file. This is completely unacceptable. You are not supposed to compile header files. Header files are supposed to be included into .cpp files, not compiled by themselves.

    Because of that problem, your d3d9_object.h, after being force-fed to compiler, produces d3d9_object.obj file, which clobbers (overwrites) the proper d3d9_object.obj file obtained from d3d9_object.cpp. This leads to LNK4042 and further linker errors.

    Don't attempt to compile d3d9_object.h. Find out how it managed to get compiled and fix the problem.