如何在头文件中替换对象?

问题描述

如何在头文件中声明和定义对象并将其用于多个静态构建的库中?考虑下面的头文件,其中我实现了一个名为Logger的类,打算在正在开发的几个库中像这样使用该类:

Utils::Logging::Logger.Display(true) << SOURCEINFO << "An exception occured: " << exp.what() << Utils::Logging::Save;

换句话说,我正在尝试实现和使用它,就像使用std :: cout一样。如您在下面看到的,我尝试了以下方法,但每次都失败:

  1. 尝试static Log Logger;会导致:
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   
  1. extern inline Log Logger;的结果:
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 
  1. 只需执行extern Log Logger;即可得出:
Severity    Code    Description Project File    Line    Suppression State
Error   LNK2005 "class std::basic_ostream<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   

在这一点上,我不确定是否正在尝试一些不可能的事情,或者只是做错了。
这是Utility.h

#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 <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <chrono>
#include <iomanip>
#include <type_traits>
#include <typeinfo>

namespace Utils
{
    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;
       }
       
       extern Log Logger;
    }
}
#endif // !UTILITY_H

我在这里想念什么?

更新

按照@Peter的回答。我做了第一个选择,即在头文件中有Log Logger;,然后在其中一个库中,例如FV.cpp我做到了:

#include <Utility.h>
...
Utils::Logging::Log Logger;

此操作失败,并显示以下错误:

Severity    Code    Description Project File    Line    Suppression State
Error   LNK2005 "class std::basic_ostream<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   

enter image description here

更新2

这是一个最小的可还原示例:https://gofile.io/d/eJeADp

解决方法

记录器需要在.cpp文件中的某个地方实例化一次。

Log Logger;

仅将其声明为外部是不够的。

#include "logger.h"

Utils::Logging::Log Logger;

void Test() {
  Logger << 42;
}
,

一个全局对象可以被声明多次,但是在整个程序中必须被定义一次。问题在于,头文件可以包含在多个编译单元(也称为源文件)中,这意味着头文件中的定义可以导致多个定义。

有一些选择。我给两个。

选项1-如果对象在文件范围内(在任何函数之外,而不是类成员),您都可以

 extern Log Logger;

在头文件中,并将该头包含在多个编译单元中。 (如果标头包含定义(例如,类定义),则通常需要使用include保护措施来防止单个编译单元多次包含标头。)

然后,在整个项目的单个编译单元中,包含该标头以获取声明,然后对其进行定义

 #include "header.h"     // for the definition of Log and the declaration extern Log Logger
 
 Log Logger;               // definition of Logger

选项2-如果对象是类的静态成员,则在类定义中(在标头中)执行

 static Log Logger;

然后在一个编译单元中执行

 #include "header.h"     // for the definition of the class or struct

 Log WhateverClass::Logger;

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...