L. Kicki
L. Kicki

Reputation: 11

C++ - Multiple definition of first defined here

I am trying to write logger.

This is my header file with logger

#include <iostream>

void logInfo(){}
void logWarning(){}
void logError(){}
void logDebug(){}

template<typename First, typename ...Rest>
void logInfo(First && first, Rest && ...rest)
{
    std::cout << "[INFO] " << std::forward<First>(first) << std::endl;
    logInfo(std::forward<Rest>(rest)...);
}

template<typename First, typename ...Rest>
void logWarning(First && first, Rest && ...rest)
{
    std::cout << "[WARNING] " << std::forward<First>(first) << std::endl;
    logWarning(std::forward<Rest>(rest)...);
}

template<typename First, typename ...Rest>
void logError(First && first, Rest && ...rest)
{
    std::cout << "[ERROR] " << std::forward<First>(first) << std::endl;
    logError(std::forward<Rest>(rest)...);
}

template<typename First, typename ...Rest>
void logDebug(First && first, Rest && ...rest)
{
    std::cout << "[DEBUG] " << std::forward<First>(first) << std::endl;
    logDebug(std::forward<Rest>(rest)...);
}

Then if I will include this logger to main it is working okay:

#include "CLogger.hpp"

using namespace std;

int main()
{   
    logInfo("something");
}

But if I'm trying to include logger in other class:

#include <iostream>
#include "CFileManager.hpp"
#include "CLogger.hpp"

const char* CFileManager::PWD_COMMAND = "PWD"; 
const std::string CFileManager::DATA_FILE_NAME = "/data.txt";

CFileManager::CFileManager()
    : mFile()
    , mPathToFile( getenv( PWD_COMMAND ) )
{
    mFile = std::ofstream{ mPathToFile + DATA_FILE_NAME };
}

CFileManager::~CFileManager()
{
    mFile.close();
}

Binary doesn't compile. This is error:

/usr/bin/ld: CFileManager.o: in function `logInfo()':
CFileManager.cpp:(.text+0x0): multiple definition of `logInfo()'; main.o:main.cpp:(.text+0x0): first defined here
/usr/bin/ld: CFileManager.o: in function `logWarning()':
CFileManager.cpp:(.text+0xb): multiple definition of `logWarning()'; main.o:main.cpp:(.text+0xb): first defined here
/usr/bin/ld: CFileManager.o: in function `logError()':
CFileManager.cpp:(.text+0x16): multiple definition of `logError()'; main.o:main.cpp:(.text+0x16): first defined here
/usr/bin/ld: CFileManager.o: in function `logDebug()':
CFileManager.cpp:(.text+0x21): multiple definition of `logDebug()'; main.o:main.cpp:(.text+0x21): first defined here

It could be the case, that I missing something in makefile?:

#folders
IMPL_DIR := src
HEADER_DIR := include
BIN_DIR := bin

#flags
CCFLAGS := -Wall -Wextra -pedantic -std=c++17
IINCLUDE := -I$(HEADER_DIR)

#output
output: main.o CFileManager.o
    g++ $(CCFLAGS) $(IINCLUDE) main.o CFileManager.o -o $(BIN_DIR)/login

#main
main.o: main.cpp | $(BIN_DIR)
    g++ $(IINCLUDE) -c main.cpp

#fileManager
CFileManager.o: $(HEADER_DIR)/CFileManager.hpp $(IMPL_DIR)/CFileManager.cpp
    g++ $(IINCLUDE) -c $(IMPL_DIR)/CFileManager.cpp 

#create bin folder
$(BIN_DIR):
    mkdir -p $@

#make clean
.PHONY: clean

clean:
    $(RM) *.o

Because I doesn't change anything in makefile, because this is only header. What's wrong?


UPDATE

Did you know how could I add more information to the log? I want to see:

  1. FILE_NAME - Name of the file where log was used
  2. FUNCTION - Function name where log was used
  3. LINE - Line in which log was used

Can I somehow modify my code easily? Or should I re implement completely?

Upvotes: 1

Views: 3343

Answers (1)

Jarod42
Jarod42

Reputation: 218278

void logInfo(){}
void logWarning(){}
void logError(){}
void logDebug(){}

Are regular functions, so if included in different translation unit, you have multiple definitions.

Marking them inline would solve the issue:

inline void logInfo(){}
inline void logWarning(){}
inline void logError(){}
inline void logDebug(){}

For your specific case, you might rewrite your template method to avoid the need of those methods BTW:

template<typename... Ts>
void logInfo(Ts&&...args)
{
    ((std::cout << "[INFO] " << std::forward<Ts>(args) << std::endl), ...);
}

Upvotes: 3

Related Questions