使用 make 构建时出错:对 `CEGUI::NullRenderer::create()' 的未定义引用

Error building with make: Undefined reference to `CEGUI::NullRenderer::create()'

最近有人让我想起了我小时候玩过的一款游戏(不久前),我想再次尝试 运行。它已经将近十年没有开发了,所以没有一个预构建的包起作用。我获取了游戏的源代码和旧版本的 CEGUI 库并开始工作。

我修复了 make 过程中的一些问题,但现在我遇到了这个错误消息:

/usr/bin/ld: smc-video.o: in function `SMC::cVideo::Init_CEGUI_Fake() const':
video.cpp:107: undefined reference to `CEGUI::NullRenderer::create()'

我对 C++ 没有什么经验,但我设法理解这是一个找不到函数声明的链接器问题。所以我查看了应该有声明的文件,确实有一个创建函数:

/*!
\brief
    Create an NullRenderer object 
*/
static NullRenderer& create();

但是它不会错过声明吗?我检查过链接器是否在正确的文件中查找函数,因为将 {} 添加到代码中确实可以防止错误(它当然会导致不同的错误),这让我相信 make 没有任何问题。我也在 CEGUINullRenderer.h 的包含中查找声明,但没有找到。 (没有CEGUINullRenderer.cpp)

我试过只从创建函数返回一个 NullRenderer 对象,但这实际上不起作用。我在这里错过了什么?

video.cpp(直到抛出错误的部分):

/***************************************************************************
 * video.cpp  -  General video functions
 *
 * Copyright (C) 2005 - 2011 Florian Richter
 ***************************************************************************/
/*
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.
   
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "../video/video.h"
#include "../gui/hud.h"
#include "../user/preferences.h"
#include "../core/framerate.h"
#include "../video/font.h"
#include "../core/game_core.h"
#include "../video/img_settings.h"
#include "../input/mouse.h"
#include "../video/renderer.h"
#include "../core/main.h"
#include "../core/math/utilities.h"
#include "../core/i18n.h"
#include "../core/math/size.h"
#include "../core/filesystem/filesystem.h"
#include "../core/filesystem/resource_manager.h"
#include "../gui/spinner.h"
// SDL
#include "SDL_opengl.h"
// CEGUI
#include "CEGUIDefaultResourceProvider.h"
#include "CEGUIDefaultLogger.h"
#include "CEGUIExceptions.h"
#include "CEGUIWindowFactoryManager.h"
#include "CEGUIImagesetManager.h"
#include "CEGUIFontManager.h"
#include "CEGUIWindowManager.h"
#include "CEGUISchemeManager.h"
#include "falagard/CEGUIFalWidgetLookManager.h"
#include "elements/CEGUIProgressBar.h"
#include "RendererModules/Null/CEGUINullRenderer.h"
// png
#include <png.h>
#ifndef PNG_COLOR_TYPE_RGBA
    #define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA
#endif

namespace SMC
{

/* *** *** *** *** *** *** *** Video class *** *** *** *** *** *** *** *** *** *** */

cVideo :: cVideo( void )
{
    m_opengl_version = 0;

    m_double_buffer = 0;

    m_rgb_size[0] = 0;
    m_rgb_size[1] = 0;
    m_rgb_size[2] = 0;

    m_default_buffer = GL_BACK;
    m_max_texture_size = 512;
    
    m_audio_init_failed = 0;
    m_joy_init_failed = 0;
    m_geometry_quality = cPreferences::m_geometry_quality_default;
    m_texture_quality = cPreferences::m_texture_quality_default;

    SDL_VERSION( &wm_info.version );
#ifdef __unix__
    glx_context = NULL;
#endif
    m_render_thread = boost::thread();

    m_initialised = 0;
}

cVideo :: ~cVideo( void )
{

}

void cVideo :: Init_CEGUI_Fake( void ) const
{
    // create fake Resource Provider
    CEGUI::DefaultResourceProvider *rp = new CEGUI::DefaultResourceProvider();
    // set Resource Provider directories
    if( CEGUI::System::getDefaultXMLParserName().compare( "XercesParser" ) == 0 )
    {
        // This is needed for Xerces to specify the schemas location
        rp->setResourceGroupDirectory( "schemas", DATA_DIR "/" GAME_SCHEMA_DIR "/" );
    }
    // get a directory to dump the CEGUI log
#ifdef _WIN32
    // fixme : Workaround for std::string to CEGUI::String utf8 conversion. Check again if CEGUI 0.8 works with std::string utf8
    CEGUI::String log_dump_dir = (const CEGUI::utf8*)((Get_Temp_Directory() + "cegui.log").c_str());
#else
    CEGUI::String log_dump_dir = "/dev/null";
#endif
    // create fake system and renderer
    pGuiSystem = &CEGUI::System::create( CEGUI::NullRenderer::create(), rp, NULL, NULL, NULL, "", log_dump_dir );
}

CEGUINullRenderer.h:

/***********************************************************************
    filename:   CEGUINullRenderer.h
    created:    Fri Jan 15 2010
    author:     Eugene Marcotte
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2010 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#ifndef _CEGUINullRenderer_h_
#define _CEGUINullRenderer_h_

#include "../../CEGUIRenderer.h"
#include "../../CEGUISize.h"
#include "../../CEGUIVector.h"

#include <vector>
#include <map>

#if (defined( __WIN32__ ) || defined( _WIN32 )) && !defined(CEGUI_STATIC)
#   ifdef NULL_GUIRENDERER_EXPORTS
#       define NULL_GUIRENDERER_API __declspec(dllexport)
#   else
#       define NULL_GUIRENDERER_API __declspec(dllimport)
#   endif
#else
#   define NULL_GUIRENDERER_API
#endif

#if defined(_MSC_VER)
#   pragma warning(push)
#   pragma warning(disable : 4251)
#endif


// Start of CEGUI namespace section
namespace CEGUI
{
class NullGeometryBuffer;
class NullTexture;
class NullRenderTarget;

//! CEGUI::Renderer implementation for no particular engine
class NULL_GUIRENDERER_API NullRenderer : public Renderer
{
public:
    /*!
    \brief
        Convenience function that creates all the necessary objects
        then initialises the CEGUI system with them.

        This will create and initialise the following objects for you:
        - CEGUI::NullRenderer
        - CEGUI::DefaultResourceProvider
        - CEGUI::System

    \return
        Reference to the CEGUI::NullRenderer object that was created.

    */
    static NullRenderer& bootstrapSystem();
    
    /*!
    \brief
        Convenience function to cleanup the CEGUI system and related objects
        that were created by calling the bootstrapSystem function.

        This function will destroy the following objects for you:
        - CEGUI::System
        - CEGUI::DefaultResourceProvider
        - CEGUI::NullRenderer

    \note
        If you did not initialise CEGUI by calling the bootstrapSystem function,
        you should \e not call this, but rather delete any objects you created
        manually.
    */
    static void destroySystem();

    /*!
    \brief
        Create an NullRenderer object 
    */
    static NullRenderer& create();

    //! destory an NullRenderer object.
    static void destroy(NullRenderer& renderer);

    // implement CEGUI::Renderer interface
    RenderingRoot& getDefaultRenderingRoot();
    GeometryBuffer& createGeometryBuffer();
    void destroyGeometryBuffer(const GeometryBuffer& buffer);
    void destroyAllGeometryBuffers();
    TextureTarget* createTextureTarget();
    void destroyTextureTarget(TextureTarget* target);
    void destroyAllTextureTargets();
    Texture& createTexture();
    Texture& createTexture(const String& filename, const String& resourceGroup);
    Texture& createTexture(const Size& size);
    void destroyTexture(Texture& texture);
    void destroyAllTextures();
    void beginRendering();
    void endRendering();
    void setDisplaySize(const Size& sz);
    const Size& getDisplaySize() const;
    const Vector2& getDisplayDPI() const;
    uint getMaxTextureSize() const;
    const String& getIdentifierString() const;

protected:
    //! default constructor.
    NullRenderer();
    //! common construction things.
    void constructor_impl();
    //! destructor.
    virtual ~NullRenderer();

    //! String holding the renderer identification text.
    static String d_rendererID;
    //! What the renderer considers to be the current display size.
    Size d_displaySize;
    //! What the renderer considers to be the current display DPI resolution.
    Vector2 d_displayDPI;
    //! The default rendering root object
    RenderingRoot* d_defaultRoot;
    //! The default RenderTarget (used by d_defaultRoot)
    NullRenderTarget* d_defaultTarget;
    //! container type used to hold TextureTargets we create.
    typedef std::vector<TextureTarget*> TextureTargetList;
    //! Container used to track texture targets.
    TextureTargetList d_textureTargets;
    //! container type used to hold GeometryBuffers we create.
    typedef std::vector<NullGeometryBuffer*> GeometryBufferList;
    //! Container used to track geometry buffers.
    GeometryBufferList d_geometryBuffers;
    //! container type used to hold Textures we create.
    typedef std::vector<NullTexture*> TextureList;
    //! Container used to track textures.
    TextureList d_textures;
    //! What the renderer thinks the max texture size is.
    uint d_maxTextureSize;
};


} // End of  CEGUI namespace section

#if defined(_MSC_VER)
#   pragma warning(pop)
#endif

#endif  // end of guard _CEGUINullRenderer_h_

你已经声明了它,这意味着编译器很乐意编译它。声明就像函数的名称:编译器知道函数存在并且可以编译调用它的代码。

但是为了link它,你必须在那个函数的定义(实现)中link。 linker 告诉你它没有你声明的函数的实际实现。

很可能你的 link 行是错误的,几乎可以肯定你根本就忘了把库放在 link 行上,或者你把它放在了错误的地方,但是因为你没有如果不向我们提供有关 运行 生成该错误消息的 link 命令的任何信息,或者您使用 link 的 makefile 规则,我们将无能为力。