Linking error with static methods - c++

I'm unable to figure out what's the problem here. I have a ConsoleIO class, which contains two methods:
static void OutputMessage(const std::string &message);
static void OutputMessageNoNewLine(const std::string &message);
They are defined inline in the header:
inline void ConsoleIO::OutputMessage(const std::string &message)
{
std::cout << message << std::endl;
}
inline void OutputMessageNoNewLine(const std::string &message)
{
std::cout << message << " ";
std::flush(std::cout);
}
Also another class, ContentParser, with the methods:
static const bool StringEquals(const char *a, const char *b);
template <typename T>
static const std::string NumToString(const T num);
template <typename T>
static const T StringToNum(const std::string &s);
Which are defined in a separate file, ContentParser.cpp.
template <typename T>
const std::string ContentParser::NumToString(const T num)
{
std::ostringstream ss;
ss << num;
return ss.str();
}
I have the following code in the AdventureGame class:
ConsoleIO::OutputMessageNoNewLine(ContentParser::NumToString(num+1));
ConsoleIO::OutputMessage(" - " + decision.choices.at(num).text);
The strange thing is, from the above, the bottom line works fine, but the top produces an error when linking (line 65 error). The StringEquals method also works everywhere.
Here is the build log:
14:03:02 **** Incremental Build of configuration Debug for project AdventureGame ****
Info: Internal Builder is used for build
g++ -O0 -g3 -Wall -c -fmessage-length=0 -std=c++11 -o AdventureGame.o "..\\AdventureGame.cpp"
g++ "-LD:\\Program Files\\tinyxml2-master" -o AdventureGame.exe tinyxml2.o Main.o ContentParser.o AdventureGame.o -ltinyxml
AdventureGame.o: In function `AdventureGame::ProcessDecision()':
D:\adat\eclipse\AdventureGame\Debug/../AdventureGame.cpp:65: undefined reference to `std::string const ContentParser::NumToString<unsigned int>(unsigned int)'
D:\adat\eclipse\AdventureGame\Debug/../AdventureGame.cpp:65: undefined reference to `ConsoleIO::OutputMessageNoNewLine(std::string const&)'
D:\adat\eclipse\AdventureGame\Debug/../AdventureGame.cpp:68: undefined reference to `ConsoleIO::OutputMessageNoNewLine(std::string const&)'
collect2.exe: error: ld returned 1 exit status
What am I missing?

This
inline void OutputMessageNoNewLine(const std::string &message)
{
std::cout << message << " ";
std::flush(std::cout);
}
should be this
inline void ConsoleIO::OutputMessageNoNewLine(const std::string &message)
{
std::cout << message << " ";
std::flush(std::cout);
}
Easy mistake to make.

Related

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:
FILE_NAME - Name of the file where log was used
FUNCTION - Function name where log was used
LINE - Line in which log was used
Can I somehow modify my code easily? Or should I re implement completely?
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), ...);
}

Emscripten: how to disable warning: explicit specialization cannot have a storage class

I am building my program by using the latest Emscripten compiler.
It is based on Clang version 14. Actually it is a small test program which is the following:
#include <iostream>
struct Test {
template<typename T>
static inline void Dump(const T& value) {
std::cout << "[generic] = '" << value << "'\n";
}
template<>
static inline void Dump<std::string>(const std::string& value) {
std::cout << "[std::string] = '" << value << "'\n";
}
};
int main() {
std::string text = "hello";
Test::Dump(text);
return 0;
}
When I build it by Emscripten compiler I got the warning:
D:\em_test>emcc a.cpp
a.cpp:10:24: warning: explicit specialization cannot have a storage class
static inline void Dump<std::string>(const std::string& value) {
~~~~~~~ ^
1 warning generated.
If I just remove static keyword from void Dump<std::string> line
then there will be no warning. However, this code will cause compilation error in Visual Studio:
D:\em_test\a.cpp(17,11): error C2352: 'Test::Dump': illegal call of non-static member function
But this error is expected and clear.
I would like to write a cross-platform program.
So, I think I should simple disable this warning in Emscripten.
However, I can not find any Emscripten (which is based on clang version 14)
command line option for that!
And I am asking advice for that.
Actually I tried to use -Wno-static-inline-explicit-instantiation command line option but it did not help:
D:\em_test>emcc -Wno-static-inline-explicit-instantiation a.cpp
a.cpp:10:24: warning: explicit specialization cannot have a storage class
static inline void Dump<std::string>(const std::string& value) {
~~~~~~~ ^
1 warning generated.
However, I see in Clang version 13 user manual description about -Wstatic-inline-explicit-instantiation option but it is about a slightly another warning text.
Also it seems that Clang version 14 is not fully released, so, there is no public Clang version 14 user manual.
I can not find any Emscripten or Clang command line option to disable the above warning.
Could somebody help me?
Explicit specialization of (both static and non-static) function templates cannot be put into class definitions.
Just put it into the enclosing namespace(i.e somewhere after the class):
#include <iostream>
struct Test {
template <typename T>
static inline void Dump(const T& value) {
std::cout << "[generic] = '" << value << "'\n";
}
};
// Notice Test::
template <>
inline void Test::Dump<std::string>(const std::string& value) {
std::cout << "[std::string] = '" << value << "'\n";
}
int main() {
std::string text = "hello";
Test::Dump(text);
return 0;
}
inline is never necessary for in-class function definitions but it has different meaning for member variables.
inline for out-class is necessary in header files because the explicit specialization is not a template anymore.

function pointers in array (nix c++)

I'm getting a compiler error when I'm trying to initialize my array with function pointers. Without using a class I'm able to run the code fine, but when I incorporate the code in a class I'm getting the error. I suppose this is more of a problem with my understanding of class usage, the scope resolution operator, etc. Any help to get this resolved would be much appreciated.
#include <iostream>
#include <cassert>
using namespace std;
#define F1 0
#define F2 1
#define F3 2
class A
{
private:
bool Func1();
bool Func2();
bool Func3();
public:
bool do_it(int op);
typedef bool (A::*fn)(void);
static fn funcs[3];
protected:
};
A::fn A::funcs[3] = {Func1, Func2, Func3};
int main()
{
A Obj;
cout << "Func1 returns " << Obj.do_it(F1) << endl;
cout << "Func2 returns " << Obj.do_it(F2) << endl;
cout << "Func3 returns " << Obj.do_it(F3) << endl;
return 0;
}
bool A::do_it(int op)
{
assert(op < 3 && op >= 0);
return (this->*(funcs[op]))();
}
bool A::Func1() { return false; }
bool A::Func2() { return true; }
bool A::Func3() { return false; }
The compiler spits out:
15:35:31 **** Build of configuration Debug for project JT ****
make all
make: Warning: File 'objects.mk' has modification time 7.3 s in the future
Building file: ../src/JT.cpp
Invoking: GCC C++ Compiler
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/JT.d" -MT"src/JT.o" -o "src/JT.o" "../src/JT.cpp"
../src/JT.cpp:141:41: error: cannot convert ‘A::Func1’ from type ‘bool (A::)()’ to type ‘A::fn {aka bool (A::*)()}’
A::fn A::funcs[3] = {Func1, Func2, Func3};
^
../src/JT.cpp:141:41: error: cannot convert ‘A::Func2’ from type ‘bool (A::)()’ to type ‘A::fn {aka bool (A::*)()}’
../src/JT.cpp:141:41: error: cannot convert ‘A::Func3’ from type ‘bool (A::)()’ to type ‘A::fn {aka bool (A::*)()}’
src/subdir.mk:18: recipe for target 'src/JT.o' failed
make: *** [src/JT.o] Error 1
15:35:32 Build Finished (took 1s.64ms)
Use A::fn A::funcs[3] = {&A::Func1, &A::Func2, &A::Func3};

Invalid use of non-static data member for accesing array in a struct

I am trying to work with JUCE Demo, and extract portions of BinaryData.cpp and OpenGLDemo.cpp into my own class. Unfortunately, I run into a problem I can't really parse, which I've managed to reduce in this minimal example of three files: main.cpp, mystuff.cpp and mystuff.h:
main.cpp:
// g++ -std=c++11 main.cpp mystuff.cpp -o main
#include "mystuff.h"
int main(int argc, char* argv[])
{
MyStuff tmpstuff;
std::cout << "hello world" << tmpstuff.temp_binary_data_7[0] << std::endl ;
}
mystuff.h
#include <iostream>
class MyStuff
{
public:
MyStuff();
~MyStuff();
// from BinaryData.cpp:
//~ static const unsigned char temp_binary_data_7[] =
//~ { 35,32,77,97,120,50,79,98,106,32,86,101,114,115,105,111,110,32,52,46,48,32,77,97,114,32,49,48,116,104,44,32,50,48,48,49,10,35,10,35,32,111,98,106,101,99,116,32,84,101,97,112,111,116,48,49,32,116,111,32,99,111,109,101,32,46,46,46,10,35,10,118,32,32,53,
//~ 46,57,50,57,54,56,56,32,52,46,49,50,53,48,48,48,32,48,46,48,48,48,48,48,48,10,118,32,32,53,46,56,51,50,48,51,49,32,52,46,52,57,52,49,52,49,32,48,46,48,48,48,48,48,48,10,118,32,32,53,46,57,52,53,51,49,51,32,52,46,54,49,55,49,56,56,32,48,46,48,48,48,48,
//~ 48,48,10,118,32,32,54,46,49,55,53,55,56,49,32,52,46,52,57,52,49,52,49,32,48,46,48,48,48,48,48,48,10,118,32,32,54,46,52,50,57,54,56,56,32,52,46,49,50,53,48,48,48,32,48,46,48,48,48,48,48,48,10,118,32,32,53,46,51,56,55,49,56,56,32,52,46,49,50,53,48,48,48
//~ };
// move definition to .cpp because of 'error: in-class initialization of static data member ‘const unsigned char MyStuff::temp_binary_data_7 []’ of incomplete type'
static const unsigned char temp_binary_data_7[];
const char* teapot_obj = (const char*) temp_binary_data_7;
// from OpenGLDemo.cpp:
struct Shape
{
Shape()
{
std::cout << "initializing " << static_cast<void*>(teapot_obj) << std::endl ;
}
};
};
mystuff.cpp:
#include "mystuff.h"
const unsigned char MyStuff::temp_binary_data_7[] =
{ 35,32,77,97,120,50,79,98,106,32,86,101,114,115,105,111,110,32,52,46,48,32,77,97,114,32,49,48,116,104,44,32,50,48,48,49,10,35,10,35,32,111,98,106,101,99,116,32,84,101,97,112,111,116,48,49,32,116,111,32,99,111,109,101,32,46,46,46,10,35,10,118,32,32,53,
46,57,50,57,54,56,56,32,52,46,49,50,53,48,48,48,32,48,46,48,48,48,48,48,48,10,118,32,32,53,46,56,51,50,48,51,49,32,52,46,52,57,52,49,52,49,32,48,46,48,48,48,48,48,48,10,118,32,32,53,46,57,52,53,51,49,51,32,52,46,54,49,55,49,56,56,32,48,46,48,48,48,48,
48,48,10,118,32,32,54,46,49,55,53,55,56,49,32,52,46,52,57,52,49,52,49,32,48,46,48,48,48,48,48,48,10,118,32,32,54,46,52,50,57,54,56,56,32,52,46,49,50,53,48,48,48,32,48,46,48,48,48,48,48,48,10,118,32,32,53,46,51,56,55,49,56,56,32,52,46,49,50,53,48,48,48
};
MyStuff::MyStuff() {
}
MyStuff::~MyStuff() {
}
When I compile with g++, I get this:
$ g++ -std=c++11 main.cpp mystuff.cpp -o main
In file included from main.cpp:3:0:
mystuff.h: In constructor ‘MyStuff::Shape::Shape()’:
mystuff.h:18:42: error: invalid use of non-static data member ‘MyStuff::teapot_obj’
const char* teapot_obj = (const char*) temp_binary_data_7;
^
mystuff.h:25:58: error: from this location
std::cout << "initializing " << static_cast<void*>(teapot_obj) << std::endl ;
^
In file included from mystuff.cpp:1:0:
mystuff.h: In constructor ‘MyStuff::Shape::Shape()’:
mystuff.h:18:42: error: invalid use of non-static data member ‘MyStuff::teapot_obj’
const char* teapot_obj = (const char*) temp_binary_data_7;
^
mystuff.h:25:58: error: from this location
std::cout << "initializing " << static_cast<void*>(teapot_obj) << std::endl ;
^
This happens only when the struct Shape code exists in mystuff.h - if you delete it, then the code compiles and runs fine.
So what are my options? How can I define the struct Shape (or the other variables) so that it can refer to teapot_obj without compilation errors?
Ok, managed to fix it by just throwing expressions here and there, but it would still be great to read an answer that explains what is actually going on... here are my changes - only in mystuff.h:
mystuff.h:
#include <iostream>
class MyStuff
{
public:
MyStuff();
~MyStuff();
static const unsigned char temp_binary_data_7[];
static constexpr const char* teapot_obj = (const char*) temp_binary_data_7;
// from OpenGLDemo.cpp:
struct Shape
{
Shape()
{
std::cout << "initializing " << static_cast<const void*>(teapot_obj) << std::endl ;
}
};
Shape tmptest;
};
So, basically:
const char* teapot_obj = (const char*) temp_binary_data_7;
had to change into:
static constexpr const char* teapot_obj = (const char*) temp_binary_data_7;
... which then means I have to make a static_cast<const void*> (instead of just static_cast<void*>) to print out the object address; and finally, have to add a Shape tmptest; so that the constructor of Shape runs at least once, so we can have something printed.
And now, the program runs without problems:
$ g++ -std=c++11 main.cpp mystuff.cpp -o main
$ ./main
initializing 0x400c60
hello world#

Missing Vtable when linking .o files

I am writing a simple server program using ICE by ZeroC. When I try to link the .o files it gave me the following error message:
$ c++ -o server UserMap.o Server.o -L/Library/Developer/Ice-3.5.0/lib -lIce -lIceUtil
Undefined symbols for architecture x86_64:
"VTT for UserMapI", referenced from:
UserMapI::UserMapI() in Server.o
"vtable for UserMapI", referenced from:
UserMapI::UserMapI() in Server.o
NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
After some googling I understand that the problem is I have an abstract class with 3 virtual methods declared in UserMap.ice (and hence in UserMap.h and UserMap.cpp generated by the command slice2cpp UserMap.ice), and in Server.cpp I have a class called UserMapI:public UserMap which implements the three virtual methods and another private helper function. The error is generated because the compiler thinks I have declared all functions(methods) in UserMap.h and UserMap.cpp.
My understanding to this problem is that I should modify the link command so that the linker will know that there are more functions in UserMapI declared in Server.cpp, but I don't have enough knowledge to do the modification. Can someone help me please?
Thank you all.
Here is the compiler command I am using to get Server.o and UserMap.o:
c++ -I. -I/Library/Developer/Ice-3.5.0/include -c UserMap.cpp Server.cpp
Here's the code of UserMap.ice:
module DR
{
class UserMap
{
void addUserToLocation(int userID, int x, int y);
string getUsersNearLocation(int x, int y, int distance);
void removeFromMap(int userID, int x, int y);
};
};
slice2cpp command slices this .ice file into a .h and a .cpp file that works as an API between server and client.
In Server.cpp I have the following include:
#include <Ice/Ice.h>
#include "UserMap.h"
#include <map>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
and the following subclass:
class UserMapI : public UserMap
{
public:
virtual void addUserToLocation(int userID, int x, int y, const Ice::Current &);
virtual string getUsersNearLocation(int x, int y, int distance, const Ice::Current &);
virtual void removeFromMap(int userID, int x, int y, const Ice::Current &);
private:
string stringify(int x, int y);
};
And after implementing all methods here's the main function:
int main(int argc, char* argv[])
{
int status = 0;
Ice::CommunicatorPtr ic;
try {
ic = Ice::initialize(argc, argv);
Ice::ObjectAdapterPtr adapter = ic->createObjectAdapterWithEndpoints("SimpleUserMapAdapter", "default -p 10000");
Ice::ObjectPtr object = new UserMapI;
adapter->add(object, ic->stringToIdentity("SimpleUserMap"));
adapter->activate();
ic->waitForShutdown();
} catch (const Ice::Exception & e) {
cerr << e << endl;
status = 1;
} catch (const char * msg) {
cerr << msg << endl;
status = 1;
}
if (ic){
try {
ic->destroy();
} catch (const Ice::Exception & e) {
cerr << e << endl;
status = 1;
}
}
return status;
}
Here's the UserMap.h.