when Selectively Exporting C Functions to lua in Member functions of two classes in the same namespace, the compiler report a linking error

Issue #9 closed
Oscar Zhao
created an issue

The circumstance is such: There are several C++ functions to export to lua and two classes ( LuaWrapper, YourLuaWrapper). In the member functions of LuaWrapper( and YourLuaWrapper), I want to export the C++ functions defined before. Then I put the OOLUA_CFUNC(...) in a single header file, and let the LuaWrapper.cpp and YourLuaWrapper.cpp include the header file. There are corresponding "OOLUA::set_global(..)" in the definition of member functions. <br />Before this, I always put OOLUA_CFUNC and OOLUA::set_global in the same cpp file, and no problems found. The error is the following:

1>------ Build started: Project: ExportCFuncInFunctions, Configuration: Debug Win32 ------
1>Compiling...
1>LuaWrapper.cpp
1>YourLuaWrapper.cpp
1>Generating Code...
1>Linking...
1>YourLuaWrapper.obj : error LNK2005: "bool __cdecl ttservice::isBool(void)" (?isBool@ttservice@@YA_NXZ) already defined in LuaWrapper.obj
1>YourLuaWrapper.obj : error LNK2005: "bool __cdecl ttservice::isInt(void)" (?isInt@ttservice@@YA_NXZ) already defined in LuaWrapper.obj
1>YourLuaWrapper.obj : error LNK2005: "bool __cdecl ttservice::isDouble(void)" (?isDouble@ttservice@@YA_NXZ) already defined in LuaWrapper.obj
1>YourLuaWrapper.obj : error LNK2005: "bool __cdecl ttservice::isString(void)" (?isString@ttservice@@YA_NXZ) already defined in LuaWrapper.obj
1>YourLuaWrapper.obj : error LNK2005: "int __cdecl ttservice::oolua_isBool(struct lua_State *)" (?oolua_isBool@ttservice@@YAHPAUlua_State@@@Z) already defined in LuaWrapper.obj
1>YourLuaWrapper.obj : error LNK2005: "int __cdecl ttservice::oolua_isInt(struct lua_State *)" (?oolua_isInt@ttservice@@YAHPAUlua_State@@@Z) already defined in LuaWrapper.obj
1>YourLuaWrapper.obj : error LNK2005: "int __cdecl ttservice::oolua_isDouble(struct lua_State *)" (?oolua_isDouble@ttservice@@YAHPAUlua_State@@@Z) already defined in LuaWrapper.obj
1>YourLuaWrapper.obj : error LNK2005: "int __cdecl ttservice::oolua_isString(struct lua_State *)" (?oolua_isString@ttservice@@YAHPAUlua_State@@@Z) already defined in LuaWrapper.obj
1>E:\work\Git\OOLua\OOLuaTest\_runtime\Debug\\ExportCFuncInFunctions.exe : fatal error LNK1169: one or more multiply defined symbols found
1>Build log was saved at "file://E:\work\Git\OOLua\OOLuaTest\_builder\Debug\ExportCFuncInFunctions\BuildLog.htm"
1>ExportCFuncInFunctions - 9 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

SOURCE Code: FunctionsToExport.h

#ifndef FUNCTIONS_TO_EXPORT_H__
#define FUNCTIONS_TO_EXPORT_H__

#include "oolua.h"

namespace ttservice
{
    bool isBool(){ return true; }
    bool isInt(){ return true; }
    bool isDouble(){ return true; }
    bool isString(){ return true; }
}
#endif

FunctionsRegist.h

#ifndef FUNCTION_REGIST_H_
#define FUNCTION_REGIST_H_

#include "oolua.h"
#include "FunctionsToExport.h"

namespace ttservice
{
    OOLUA_CFUNC(isBool, oolua_isBool)
    OOLUA_CFUNC(isInt, oolua_isInt)
    OOLUA_CFUNC(isDouble, oolua_isDouble)
    OOLUA_CFUNC(isString, oolua_isString)
}   
#endif

LuaWrapper.h

#ifndef LUA_WRAPPER_H_
#define LUA_WRAPPER_H_

#include "oolua.h"

namespace ttservice
{
    class LuaWrapper
    {
    public:
        LuaWrapper(): vm(new OOLUA::Script){}
        ~LuaWrapper();

        void regist_one_function();
        void regist_all_functions();

    private:
        OOLUA::Script* vm;
    };
}
#endif

LuaWrapper.cpp

#include "LuaWrapper.h"
#include "oolua.h"
#include "FunctionsRegist.h"

namespace ttservice
{
    LuaWrapper::~LuaWrapper()
    {
        if(vm) delete vm; 
    }

    void LuaWrapper::regist_one_function()
    {
        OOLUA::set_global(*vm, "isBool", oolua_isBool);
    }

    void LuaWrapper::regist_all_functions()
    {
        OOLUA::set_global(*vm, "isBool", oolua_isBool);
        OOLUA::set_global(*vm, "isInt", oolua_isInt);
        OOLUA::set_global(*vm, "isDouble", oolua_isDouble);
        OOLUA::set_global(*vm, "isString", oolua_isString);
    }
}

YourLuaWrapper.h

#ifndef YOUR_LUA_WRAPPER_H_
#define YOUR_LUA_WRAPPER_H_

#include "oolua.h"

namespace ttservice
{
    class YourLuaWrapper
    {
        YourLuaWrapper():vm(new OOLUA::Script){}
        ~YourLuaWrapper();
        void regist_one_function();
        void regist_all_functions();

    private:
        OOLUA::Script* vm;
    };
}   
#endif

YourLuaWrapper.cpp

#include "YourLuaWrapper.h"
#include "oolua.h"
#include "FunctionsRegist.h"

namespace ttservice
{
    YourLuaWrapper::~YourLuaWrapper()
    {
        if(vm) delete vm; 
    }

    void YourLuaWrapper::regist_one_function()
    {
        OOLUA::set_global(*vm, "isBool", oolua_isBool);
    }

    void YourLuaWrapper::regist_all_functions()
    {
        OOLUA::set_global(*vm, "isBool", oolua_isBool);
        OOLUA::set_global(*vm, "isInt", oolua_isInt);
        OOLUA::set_global(*vm, "isDouble", oolua_isDouble);
        OOLUA::set_global(*vm, "isString", oolua_isString);
    }
}

Comments (4)

  1. Liam Devine repo owner

    Yes, this is a linker error. OOLUA_CFUNC will define a wrapper function for the function in question. As you use OOLUA_CFUNC is a header file it is breaking the single definition rule of C++ and therefore resulting in an error that the symbol is found in multiple translation units. Either forward declare the function in the header, move the OOLUA_CFUNC to the source file or maybe prefix the macro with "inline".

  2. Oscar Zhao reporter

    From your reply, I get to know something about how OOLUA_CFUNC works. Could I think OOLUA_CFUNC defines new C functions in default, adding "inline" defines new member functions? I don't quite understand your suggestions. The first interpretation "putting the definition of isBool, isInt, isDouble, etc. in the header file (LuaWrapper.h, YourLuaWrapper.h) and inside the class definition(LuaWrapper, YourLuaWraper) separately and putting OOLUA_CFUNC(s) in the source file(LuaWrapper.cpp, YourLuaWrapper.cpp)", so isBool, isInt, etc. become the member functions of LuaWrapper and YourLuaWrapper, which goes against the original intention (exporting C functions selectively); My second interpretation is "putting the definition of isBool, isInt, isDouble, etc. in the header file (LuaWrapper.h, YourLuaWrapper.h) outside the class definitions", then isBool, isInt, etc. will be defined for more than one time in the same namespace(ttservice), not to say exporting them. I tried the second advice by putting adding "inline" to OOLUA_CFUNC, it report the following error:

    1>------ Build started: Project: ExportCFuncInFunctions, Configuration: Debug Win32 ------
    1>Linking...
    1>YourLuaWrapper.obj : error LNK2005: "bool __cdecl ttservice::isBool(void)" (?isBool@ttservice@@YA_NXZ) already defined in LuaWrapper.obj
    1>YourLuaWrapper.obj : error LNK2005: "bool __cdecl ttservice::isInt(void)" (?isInt@ttservice@@YA_NXZ) already defined in LuaWrapper.obj
    1>YourLuaWrapper.obj : error LNK2005: "bool __cdecl ttservice::isDouble(void)" (?isDouble@ttservice@@YA_NXZ) already defined in LuaWrapper.obj
    1>YourLuaWrapper.obj : error LNK2005: "bool __cdecl ttservice::isString(void)" (?isString@ttservice@@YA_NXZ) already defined in LuaWrapper.obj
    1>E:\work\Git\OOLua\OOLuaTest\_runtime\Debug\\ExportCFuncInFunctions.exe : fatal error LNK1169: one or more multiply defined symbols found
    1>Build log was saved at "file://E:\work\Git\OOLua\OOLuaTest\_builder\Debug\ExportCFuncInFunctions\BuildLog.htm"
    1>ExportCFuncInFunctions - 5 error(s), 0 warning(s)
    ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
    

    FunctionsRegist.h:

    #ifndef FUNCTION_REGIST_H_
    #define FUNCTION_REGIST_H_
    
    #include "oolua.h"
    #include "FunctionsToExport.h"
    
    namespace ttservice
    {
        inline OOLUA_CFUNC(isBool, oolua_isBool)
        inline OOLUA_CFUNC(isInt, oolua_isInt)
        inline OOLUA_CFUNC(isDouble, oolua_isDouble)
        inline OOLUA_CFUNC(isString, oolua_isString)
    }   
    #endif
    

    The last try I did is to put 'OOLUA_CFUNC(...)' inside the class definitions. The code is FunctionsToExport.h

    #ifndef FUNCTIONS_TO_EXPORT_H__
    #define FUNCTIONS_TO_EXPORT_H__
    
    #include "oolua.h"
    
    namespace ttservice
    {
        bool isBool(){ return true; }
        bool isInt(){ return true; }
        bool isDouble(){ return true; }
        bool isString(){ return true; }
    }
    #endif
    

    LuaWrapper.h

    #ifndef LUA_WRAPPER_H_
    #define LUA_WRAPPER_H_
    
    #include "oolua.h"
    #include "FunctionsToExport.h"
    namespace ttservice
    {
        class LuaWrapper
        {
        public:
            LuaWrapper(): vm(new OOLUA::Script){}
            ~LuaWrapper();
    
            OOLUA_CFUNC(isBool, oolua_isBool)
            OOLUA_CFUNC(isInt, oolua_isInt)
            OOLUA_CFUNC(isDouble, oolua_isDouble)
            OOLUA_CFUNC(isString, oolua_isString)
    
            void regist_one_function();
            void regist_all_functions();
    
        private:
            OOLUA::Script* vm;
        };
    }
    #endif
    

    LuaWrapper.cpp

    #include "LuaWrapper.h"
    #include "oolua.h"
    //#include "FunctionsRegist.h"
    
    namespace ttservice
    {
        LuaWrapper::~LuaWrapper()
        {
            if(vm) delete vm; 
        }
    
        void LuaWrapper::regist_one_function()
        {
            OOLUA::set_global(*vm, "isBool", oolua_isBool);
        }
    
        void LuaWrapper::regist_all_functions()
        {
            OOLUA::set_global(*vm, "isBool", oolua_isBool);
            OOLUA::set_global(*vm, "isInt", oolua_isInt);
            OOLUA::set_global(*vm, "isDouble", oolua_isDouble);
            OOLUA::set_global(*vm, "isString", oolua_isString);
        }
    }
    

    YourLuaWrapper.h

    #ifndef YOUR_LUA_WRAPPER_H_
    #define YOUR_LUA_WRAPPER_H_
    
    #include "oolua.h"
    #include "FunctionsToExport.h"
    namespace ttservice
    {
        class YourLuaWrapper
        {
            YourLuaWrapper():vm(new OOLUA::Script){}
            ~YourLuaWrapper();
            OOLUA_CFUNC(isBool, oolua_isBool)
            OOLUA_CFUNC(isInt, oolua_isInt)
            OOLUA_CFUNC(isDouble, oolua_isDouble)
            OOLUA_CFUNC(isString, oolua_isString)
            void regist_one_function();
            void regist_all_functions();
    
        private:
            OOLUA::Script* vm;
        };
    }   
    #endif
    

    YourLuaWrapper.cpp

    #include "YourLuaWrapper.h"
    #include "oolua.h"
    //#include "FunctionsRegist.h"
    
    namespace ttservice
    {
        YourLuaWrapper::~YourLuaWrapper()
        {
            if(vm) delete vm; 
        }
    
        void YourLuaWrapper::regist_one_function()
        {
            OOLUA::set_global(*vm, "isBool", oolua_isBool);
        }
    
        void YourLuaWrapper::regist_all_functions()
        {
            OOLUA::set_global(*vm, "isBool", oolua_isBool);
            OOLUA::set_global(*vm, "isInt", oolua_isInt);
            OOLUA::set_global(*vm, "isDouble", oolua_isDouble);
            OOLUA::set_global(*vm, "isString", oolua_isString);
        }
    }
    

    The error msg is:

    1>------ Build started: Project: ExportCFuncInFunctions, Configuration: Debug Win32 ------
    1>Compiling...
    1>LuaWrapper.cpp
    1>e:\work\git\oolua\ooluatest\src\exportcfuncinfunctions\luawrapper.cpp(14) : error C3867: 'ttservice::LuaWrapper::oolua_isBool': function call missing argument list; use '&ttservice::LuaWrapper::oolua_isBool' to create a pointer to member
    1>e:\work\git\oolua\ooluatest\src\exportcfuncinfunctions\luawrapper.cpp(19) : error C3867: 'ttservice::LuaWrapper::oolua_isBool': function call missing argument list; use '&ttservice::LuaWrapper::oolua_isBool' to create a pointer to member
    1>e:\work\git\oolua\ooluatest\src\exportcfuncinfunctions\luawrapper.cpp(20) : error C3867: 'ttservice::LuaWrapper::oolua_isInt': function call missing argument list; use '&ttservice::LuaWrapper::oolua_isInt' to create a pointer to member
    1>e:\work\git\oolua\ooluatest\src\exportcfuncinfunctions\luawrapper.cpp(21) : error C3867: 'ttservice::LuaWrapper::oolua_isDouble': function call missing argument list; use '&ttservice::LuaWrapper::oolua_isDouble' to create a pointer to member
    1>e:\work\git\oolua\ooluatest\src\exportcfuncinfunctions\luawrapper.cpp(22) : error C3867: 'ttservice::LuaWrapper::oolua_isString': function call missing argument list; use '&ttservice::LuaWrapper::oolua_isString' to create a pointer to member
    1>YourLuaWrapper.cpp
    1>e:\work\git\oolua\ooluatest\src\exportcfuncinfunctions\yourluawrapper.cpp(14) : error C3867: 'ttservice::YourLuaWrapper::oolua_isBool': function call missing argument list; use '&ttservice::YourLuaWrapper::oolua_isBool' to create a pointer to member
    1>e:\work\git\oolua\ooluatest\src\exportcfuncinfunctions\yourluawrapper.cpp(19) : error C3867: 'ttservice::YourLuaWrapper::oolua_isBool': function call missing argument list; use '&ttservice::YourLuaWrapper::oolua_isBool' to create a pointer to member
    1>e:\work\git\oolua\ooluatest\src\exportcfuncinfunctions\yourluawrapper.cpp(20) : error C3867: 'ttservice::YourLuaWrapper::oolua_isInt': function call missing argument list; use '&ttservice::YourLuaWrapper::oolua_isInt' to create a pointer to member
    1>e:\work\git\oolua\ooluatest\src\exportcfuncinfunctions\yourluawrapper.cpp(21) : error C3867: 'ttservice::YourLuaWrapper::oolua_isDouble': function call missing argument list; use '&ttservice::YourLuaWrapper::oolua_isDouble' to create a pointer to member
    1>e:\work\git\oolua\ooluatest\src\exportcfuncinfunctions\yourluawrapper.cpp(22) : error C3867: 'ttservice::YourLuaWrapper::oolua_isString': function call missing argument list; use '&ttservice::YourLuaWrapper::oolua_isString' to create a pointer to member
    1>Generating Code...
    1>Build log was saved at "file://E:\work\Git\OOLua\OOLuaTest\_builder\Debug\ExportCFuncInFunctions\BuildLog.htm"
    1>ExportCFuncInFunctions - 10 error(s), 0 warning(s)
    ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
    

    I really do not know what to do next

  3. Liam Devine repo owner

    Adding inline was a suggestion which I have not tried before. However, you seem to not understand the difference between a forward declaration and the definintion of a function. Have a look at the unit test source code; in which it is forward declared[1] and then defined[2]

    [1] https://bitbucket.org/liamdevine/oolua/src/a6415fb55cb167441f682b3f7bd97b71153f821f/unit_tests/bind_classes/expose_static_and_c_functions.h?at=OOLua-2 [2] https://bitbucket.org/liamdevine/oolua/src/a6415fb55cb167441f682b3f7bd97b71153f821f/unit_tests/bind_classes/expose_static_and_c_functions.cpp?at=OOLua-2

  4. Log in to comment