How to prevent already defined objects error when linking two statically built libs that use the same header file?

感情迁移 提交于 2021-01-29 05:39:35

问题


I have several libraries that I want to statically link together. Let's call them Lib1, Lib2, Lib3 and Lib4. Lib4 uses Lib1 and Lib3 (Lib1 internally uses Lib2).

I can easily create the DLLs and all work just fine. the issue however is, when I try to build them as static libs I get the already defined objects error which are given bellow.

I get these errors when I include Utility.h header file which contains some utility functions that can be used in several libs. For now I am forced to not use it in except like Lib4 in order to not face any issues in static builds. This is not good and just nullifies the whole point of creating Utility.h. I did include the header guards, but it seems its irrelevant here and does not help.

What are my options here? How should I go about this to get this solved? Errors I get:

Severity    Code    Description Project File    Line    Suppression State
Error   LNK2005 "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl Utils::Logging::operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Utils::Logging::Log const &)" (??6Logging@Utils@@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV23@AEBVLog@01@@Z) already defined in FV.lib(FV.obj)   FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "class Utils::Logging::Log Utils::Logging::Logger" (?Logger@Logging@Utils@@3VLog@12@A) already defined in FV.lib(FV.obj)    FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "class std::vector<class std::filesystem::path,class std::allocator<class std::filesystem::path> > __cdecl Utils::GetFileNames(class std::vector<class std::filesystem::path,class std::allocator<class std::filesystem::path> >)" (?GetFileNames@Utils@@YA?AV?$vector@Vpath@filesystem@std@@V?$allocator@Vpath@filesystem@std@@@3@@std@@V23@@Z) already defined in FV.lib(FV.obj)  FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "class std::vector<class std::filesystem::path,class std::allocator<class std::filesystem::path> > __cdecl Utils::GetFiles(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >,bool)" (?GetFiles@Utils@@YA?AV?$vector@Vpath@filesystem@std@@V?$allocator@Vpath@filesystem@std@@@3@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@V?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@3@_N@Z) already defined in FV.lib(FV.obj)  FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "class std::vector<class std::filesystem::path,class std::allocator<class std::filesystem::path> > __cdecl Utils::GetFiles(class std::filesystem::path,class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >,bool)" (?GetFiles@Utils@@YA?AV?$vector@Vpath@filesystem@std@@V?$allocator@Vpath@filesystem@std@@@3@@std@@Vpath@filesystem@3@V?$vector@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$allocator@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@@3@_N@Z) already defined in FV.lib(FV.obj)  FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "class std::vector<class std::filesystem::path,class std::allocator<class std::filesystem::path> > __cdecl Utils::GetDirectories(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,bool)" (?GetDirectories@Utils@@YA?AV?$vector@Vpath@filesystem@std@@V?$allocator@Vpath@filesystem@std@@@3@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@_N@Z) already defined in FV.lib(FV.obj)  FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "bool __cdecl Utils::NotIn(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?NotIn@Utils@@YA_NV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z) already defined in FV.lib(FV.obj)    FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "double __cdecl Utils::Timer::GetSeconds(class std::chrono::time_point<struct std::chrono::steady_clock,class std::chrono::duration<__int64,struct std::ratio<1,1000000000> > >,class std::chrono::time_point<struct std::chrono::steady_clock,class std::chrono::duration<__int64,struct std::ratio<1,1000000000> > >)" (?GetSeconds@Timer@Utils@@YANV?$time_point@Usteady_clock@chrono@std@@V?$duration@_JU?$ratio@$00$0DLJKMKAA@@std@@@23@@chrono@std@@0@Z) already defined in FV.lib(FV.obj)    FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "double __cdecl Utils::Timer::GetElapsedTime(class std::chrono::time_point<struct std::chrono::steady_clock,class std::chrono::duration<__int64,struct std::ratio<1,1000000000> > >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,enum Utils::Timer::Duration,bool)" (?GetElapsedTime@Timer@Utils@@YANV?$time_point@Usteady_clock@chrono@std@@V?$duration@_JU?$ratio@$00$0DLJKMKAA@@std@@@23@@chrono@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@W4Duration@12@_N@Z) already defined in FV.lib(FV.obj)    FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "double __cdecl Utils::Timer::GetElapsedNanoSeconds(class std::chrono::time_point<struct std::chrono::steady_clock,class std::chrono::duration<__int64,struct std::ratio<1,1000000000> > >)" (?GetElapsedNanoSeconds@Timer@Utils@@YANV?$time_point@Usteady_clock@chrono@std@@V?$duration@_JU?$ratio@$00$0DLJKMKAA@@std@@@23@@chrono@std@@@Z) already defined in FV.lib(FV.obj)  FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "double __cdecl Utils::Timer::GetElapsedMicroSeconds(class std::chrono::time_point<struct std::chrono::steady_clock,class std::chrono::duration<__int64,struct std::ratio<1,1000000000> > >)" (?GetElapsedMicroSeconds@Timer@Utils@@YANV?$time_point@Usteady_clock@chrono@std@@V?$duration@_JU?$ratio@$00$0DLJKMKAA@@std@@@23@@chrono@std@@@Z) already defined in FV.lib(FV.obj)    FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "double __cdecl Utils::Timer::GetElapsedMilliSeconds(class std::chrono::time_point<struct std::chrono::steady_clock,class std::chrono::duration<__int64,struct std::ratio<1,1000000000> > >)" (?GetElapsedMilliSeconds@Timer@Utils@@YANV?$time_point@Usteady_clock@chrono@std@@V?$duration@_JU?$ratio@$00$0DLJKMKAA@@std@@@23@@chrono@std@@@Z) already defined in FV.lib(FV.obj)    FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "class std::chrono::time_point<struct std::chrono::steady_clock,class std::chrono::duration<__int64,struct std::ratio<1,1000000000> > > __cdecl Utils::Timer::Now(void)" (?Now@Timer@Utils@@YA?AV?$time_point@Usteady_clock@chrono@std@@V?$duration@_JU?$ratio@$00$0DLJKMKAA@@std@@@23@@chrono@std@@XZ) already defined in FV.lib(FV.obj)   FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "void __cdecl Utils::DateTime::Sleep(unsigned long)" (?Sleep@DateTime@Utils@@YAXK@Z) already defined in FV.lib(FV.obj)  FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "double __cdecl Utils::DateTime::GetSeconds(class std::chrono::time_point<struct std::chrono::system_clock,class std::chrono::duration<__int64,struct std::ratio<1,10000000> > >,class std::chrono::time_point<struct std::chrono::system_clock,class std::chrono::duration<__int64,struct std::ratio<1,10000000> > >)" (?GetSeconds@DateTime@Utils@@YANV?$time_point@Usystem_clock@chrono@std@@V?$duration@_JU?$ratio@$00$0JIJGIA@@std@@@23@@chrono@std@@0@Z) already defined in FV.lib(FV.obj)    FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Utils::DateTime::Now(bool)" (?Now@DateTime@Utils@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@_N@Z) already defined in FV.lib(FV.obj)    FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "class std::chrono::time_point<struct std::chrono::system_clock,class std::chrono::duration<__int64,struct std::ratio<1,10000000> > > __cdecl Utils::DateTime::Now(void)" (?Now@DateTime@Utils@@YA?AV?$time_point@Usystem_clock@chrono@std@@V?$duration@_JU?$ratio@$00$0JIJGIA@@std@@@23@@chrono@std@@XZ) already defined in FV.lib(FV.obj) FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2005 "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Utils::operator*(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,unsigned int)" (??DUtils@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@I@Z) already defined in FV.lib(FV.obj)    FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK1169 one or more multiply defined symbols found  FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\x64\Release\FV_Test_Lib.exe 1   

And this is how my Utility.h looks like :

#ifndef UTILITY_H
#define UTILITY_H

/* If we are we on Windows, we want a single define for it.*/
#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__))
#define _WIN32 //for both 32 and 64 bit. use _WIN64 for 64 bit only
#endif // _WIN32 

#if defined(__GNUC__) || defined(unix) || defined(__unix__) || defined(__unix)
# define _UNIX 
#endif // _UNIX

#include <algorithm> 
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <chrono>
#include <thread>
#include <cmath>
#include <iomanip>
#include <type_traits>
#include <bitset>
#include <typeinfo>

using namespace std::chrono;
using namespace std::chrono_literals;
typedef std::chrono::system_clock Time;
typedef std::chrono::milliseconds ms;
typedef std::chrono::duration<float> fsec;

#include <filesystem>
namespace fs = std::filesystem;

namespace Utils
{
    const double PI = 3.141592653589793;
    const double e = 2.7182818284590452353602874713527;
    typedef unsigned char byte;

    // to compare floats
    template<class T>
    typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type
    almost_equal(T x, T y, int ulp)
    {
        // the machine epsilon has to be scaled to the magnitude of the values used
        // and multiplied by the desired precision in ULPs (units in the last place)
        return std::fabs(x - y) <= std::numeric_limits<T>::epsilon() * std::fabs(x + y) * ulp
            // unless the result is subnormal
            || std::fabs(x - y) < std::numeric_limits<T>::min();
    }

    //overloading the * operator for string, so that we can do "*"*5 
    // like the way we can in python
    std::string operator * (std::string a, unsigned int b)
    {
        std::string output = "";
        while (b--) {
            output += a;
        }
        return output;
    }

    //ref https://github.com/Coderx7/C-Python-like-Decorators
    template<typename F>
    auto BenchmarkFunc(const F& func)
    {
        return [func](auto&&... args)
        {
            std::cout << "*******" << std::endl;
            auto start = std::chrono::high_resolution_clock::now();
            auto var = func(std::forward<decltype(args)>(args)...);
            auto end = std::chrono::high_resolution_clock::now();
            std::chrono::duration<double, std::milli> elapsed_ms = end - start;
            std::cout << "elapsed time : " << elapsed_ms.count() << " ms" << std::endl;
            std::cout << std::flush << "\n*******" << std::endl;
            return var;
        };
    }

    template<typename F>
    auto BenchmarkMethod(const F& func)
    {
        return [func](auto& obj, std::string msg, auto&&... args)
        {
            std::cout << "*******" << msg << "*******" << std::endl;
            auto start = std::chrono::high_resolution_clock::now();
            auto var = (obj.*func)(std::forward<decltype(args)>(args)...);
            auto end = std::chrono::high_resolution_clock::now();
            std::chrono::duration<double, std::milli> elapsed_ms = end - start;
            std::cout << "elapsed time : " << elapsed_ms.count() << " ms" << std::endl;
            std::cout << std::flush << "**************" << std::string("*") * msg.size() << std::endl;
            return var;
        };
    }

    namespace DateTime
    {
        std::chrono::system_clock::time_point Now()
        {
            return std::chrono::system_clock::now();
        }

        std::string Now(bool time_only)
        {
            auto current_time = Now();
            std::time_t t = std::chrono::system_clock::to_time_t(current_time);
            std::tm* tm = localtime(&t);
            std::stringstream ss;
            //%T is  HH:mm:ss  and %F is YYYY-MM-DD 
            std::string mode = time_only ? "%T" : "%F %T";
            ss << std::put_time(tm, mode.c_str()); // a %b %d %H:%M:%S %Y

            return ss.str();
        }

        /// <summary>
        /// Returns the difference between two time points in seconds
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        double GetSeconds(std::chrono::system_clock::time_point start, std::chrono::system_clock::time_point end)
        {
            std::chrono::duration<double, std::milli> elapsed_ms = end - start;
            return elapsed_ms.count() * 1000;
        }

        /// <summary>
        /// Sleeps for the given duration in milliseconds
        /// </summary>
        /// <param name="duration"></param>
        void Sleep(unsigned long duration)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(duration));
        }
    }

    
    /// @brief deals with machine level times used for benchmarking mostly
    namespace Timer
    {
        enum class Duration
        {
            Milli = 0,
            Micro = 1,
            Nano = 2
        };

        std::chrono::steady_clock::time_point Now()
        {
            //high_resolution_clock can be an alias to an 
            //arbitary number of classes such as steady_clock , system_clock 
            //or something else. on windows and MSVCP its steady_clock but in G++
            //linux its an alias for system_clock. 
            return std::chrono::steady_clock::now();
        }

        double GetElapsedMilliSeconds(std::chrono::steady_clock::time_point start)
        {
            auto end = std::chrono::steady_clock::now();
            std::chrono::duration<double, std::milli> milliseconds = end - start;
            return milliseconds.count();
        }

        double GetElapsedMicroSeconds(std::chrono::steady_clock::time_point start)
        {
            auto end = std::chrono::steady_clock::now();
            std::chrono::duration<double, std::micro> microseconds = end - start;
            return microseconds.count();
        }

        double GetElapsedNanoSeconds(std::chrono::steady_clock::time_point start)
        {
            auto end = std::chrono::steady_clock::now();
            std::chrono::duration<double, std::nano> nanoseconds = end - start;
            return nanoseconds.count();
        }

        /// <summary>
        /// Retruns the elapsed time in milliseconds from the given starting point
        /// to the current moment.
        /// </summary>
        /// <param name="start">starting time_point</param>
        /// <param name="message">message to display before showing the actual elapsed time</param>
        /// <param name="displayResult">whethr to display the result</param>
        /// <returns>elapsed milliseconds</returns>
        double GetElapsedTime(std::chrono::steady_clock::time_point start, std::string message = "elapsed time : ", Duration duration = Duration::Milli, bool displayResult = true)
        {
            auto end = std::chrono::steady_clock::now();
            double elapsedTime = 0;
            std::string symbol = " ms";

            switch (duration)
            {
                case Utils::Timer::Duration::Milli:
                    elapsedTime = Utils::Timer::GetElapsedMilliSeconds(start);
                    break;
                case Utils::Timer::Duration::Micro:
                    elapsedTime = Utils::Timer::GetElapsedMicroSeconds(start);
                    //micro second symbol is c2b5 which would be \xc2\xb5 but I used the textual
                    //symbol to be more readable. the console needs to support unicode (utf8) though!
                    symbol = " µs";
                    break;
                case Utils::Timer::Duration::Nano:
                    elapsedTime = Utils::Timer::GetElapsedNanoSeconds(start);
                    symbol = " ns";
                    break;
                default:
                    throw std::runtime_error("Duration not supported");
            }

            if (displayResult)
            {    //std::cout takes : 0.3425 - 0.9 ms
                std::cout << message << elapsedTime << symbol << std::endl;
            }

            return elapsedTime;
        }
        
        double GetSeconds(std::chrono::steady_clock::time_point start, std::chrono::steady_clock::time_point end)
        {
            std::chrono::duration<double, std::milli> elapsed_ms = end - start;
            return elapsed_ms.count() * 1000;
        }
    }

    /// <summary>
    /// Checks whether the input value exists in  the given list
    /// </summary>
    /// <typeparam name="F"></typeparam>
    /// <param name="value">value to be searched for</param>
    /// <param name="list">the list to be searched in</param>
    /// <returns>true if the value IS found, false otherwise</returns>
    template<typename F>
    bool In(F value, std::vector<F>& list)
    {
        auto it = std::find(list.begin(), list.end(), value);
        return (it != list.end());
    }

    template<typename F>
    int In(std::vector<F> smallList, std::vector<F>& largeList, bool returnDiff = false)
    {
        int status = returnDiff ? 0 : 1;
        for (auto v : smallList)
        {
            auto it = std::find(largeList.begin(), largeList.end(), v);
            if (returnDiff)
            {
                status += (it != largeList.end());
            }
            else
            {
                status *= (it != largeList.end());
            }
        }
        if (returnDiff)
        {
            return largeList.size() - status;
        }
        else
        {
            return status;
        }
    }

    /// <summary>
    /// Checks whether the input value does "Not" exists in the given list
    /// </summary>
    /// <typeparam name="F"></typeparam>
    /// <param name="value">value to be searched for</param>
    /// <param name="list">the list to be searched in</param>
    /// <returns>true if the value is Not found, false otherwise</returns>
    template<typename F>
    bool NotIn(F value, std::vector<F>& list)
    {
        return !In(value, list);
    }

    bool NotIn(std::string value_to_search, std::string string_to_search_in)
    {
        return string_to_search_in.find(value_to_search) != std::string::npos;
    }

    std::vector<fs::path> GetDirectories(std::string dirPath, bool nameOnly=true)
    {
        std::vector<fs::path> dirs;
        for (auto p : fs::directory_iterator(dirPath))
        {
            if (fs::is_directory(p))
            {
                if (nameOnly)
                    dirs.emplace_back(p);
                else
                    dirs.emplace_back(fs::path(dirPath) / p);
            }
        }
        return dirs;
    }

    std::vector<fs::path> GetFiles(fs::path directoryPath, std::vector<std::string> extensions = { ".jpg" }, bool excludeExtensions = false)
    {
        std::vector<fs::path> files;
        fs::path file;
        for (auto& p : fs::directory_iterator(directoryPath))
        {
            file = p.path();
            if (fs::is_regular_file(file))
            {
                if (excludeExtensions)
                {
                    if (NotIn(file.extension().string(), extensions))
                    {
                        files.emplace_back(file);
                    }
                }
                else if (In(file.extension().string(), extensions))
                {
                    files.emplace_back(file);
                }
            }
        }
        return files;
    }

    std::vector<fs::path> GetFiles(std::string directoryPath, std::vector<std::string> extensions = { ".jpg" }, bool exclude_extensions = false)
    {
        return GetFiles(fs::path(directoryPath), extensions, exclude_extensions);
    }

    std::vector<fs::path> GetFileNames(std::vector<fs::path> files)
    {
        std::vector<fs::path> res;
        for (auto& f : files)
        {
            res.emplace_back(f.filename());
        }
        return res;
    }


    namespace Logging
    {

#ifdef _WIN32
    #define FUNC_SIG __FUNCSIG__
#endif

#ifdef _UNIX
    #define FUNC_SIG __PRETTY_FUNCTION__
#endif

#define SOURCEINFO __FILE__<<":"<<__LINE__<<":"<<FUNC_SIG<<": "

        template<typename... Args>
        void LogMsg(std::string logFilename = "FVLog.txt", bool display = true, Args... args)
        {
            std::ofstream logFile;
            logFilename = (logFilename == "") ? "FVLog.txt" : logFilename;
            logFile.open(logFilename, std::ios::out | std::ios::app);
            
            logFile << DateTime::Now(false);

            if (display)
                ((std::cout << args), ...);
            //cpp17 fold-expression:, recursively send each value to be saved in our stream
            ((logFile << args), ...);
        }

        enum class Mode
        {
            Save = 0,
            Load = 1
        };
        static constexpr Mode Save = Mode::Save;

        class Log
        {
        private:
            std::stringstream stream;
            bool isNew = true;
            bool displayResults=false;
            bool benchMode = false;

        public:

            template <typename T>
            Log& operator<<(const T& value)
            {
                if (isNew)
                {
                    stream << DateTime::Now(false) << " ";
                    isNew = false;
                }

                if constexpr (std::is_same<T, Logging::Mode>::value) 
                {
                    if (value == Mode::Save)
                        Save();

                    if (displayResults)
                        std::cout << std::endl;;
                }
                else
                {
                    stream << value;
                }
                
                if (displayResults)
                    std::cout << stream.str();
                
                return *this;
            }
            
            void Save(std::string logFilename= "FVLog.txt")
            {
                isNew = true;

                if (benchMode)
                    return;

                std::ofstream logFile;
                logFilename = (logFilename == "") ? "FVLog.txt" : logFilename;
                logFile.open(logFilename, std::ios::out | std::ios::app);

                logFile << this->Get() << std::endl;
                
                this->stream.clear();
                this->stream.str("");
 
                logFile.close();
            }
            
            Log& Display(bool showOutput, bool benchMode=false)
            {
                displayResults = showOutput;
                this->benchMode = benchMode;

                return *this;
            }
            std::string Get() const
            {
                return this->stream.str();
            }

            friend std::ostream& operator<<(std::ostream& os, const Log& log);
       };

       std::ostream& operator<<(std::ostream& os, const Log& log)
       {
           os << log.Get();
           return os;
       }
       
       Log Logger;
    }
}
#endif // !UTILITY_H

Update

Thanks @Scheff and inlining all the functions in the header file, their respective linker error is gone now however, the linker error concerning Logger object in Logging namespace still exists. even making it static doesn't do any good and still I get the following error :

Severity    Code    Description Project File    Line    Suppression State
Error   LNK2005 "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl Utils::Logging::operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Utils::Logging::Log const &)" (??6Logging@Utils@@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV23@AEBVLog@01@@Z) already defined in FV.lib(FV.obj)   FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\AntiSpoofer.lib(AntiSpoofer.obj)    1   
Error   LNK2001 unresolved external symbol "class Utils::Logging::Log Utils::Logging::Logger" (?Logger@Logging@Utils@@3VLog@12@A)   FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\FV_Test_Lib\FV.lib(FV.obj)  1   
Error   LNK1120 1 unresolved externals  FV_Test_Lib D:\Codes\rika\cpp\port\LibtorchPort\x64\Release\FV_Test_Lib.exe 1   

Update 2

Using extern inline Log Logger; now results in the following error:

Severity    Code    Description Project File    Line    Suppression State
Error   C7526   'Logger': inline variable is undefined  FV  D:\Codes\fac_ver\cpp\port\LibtorchPort\Dependencies\include\Utility.h   513 

来源:https://stackoverflow.com/questions/64131310/how-to-prevent-already-defined-objects-error-when-linking-two-statically-built-l

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!