This question already has answers here:
Linking files in g++
(4 answers)
Why can templates only be implemented in the header file?
(17 answers)
Closed 8 years ago.
I am simply trying to use a function whose prototype is declared in a separate header file (ML_hash.h) and whose declaration is made in separate cpp file. I am trying to call this function in a different header file (HashNode.h). Here is the relevant code:
HashNode.h:
#ifndef HASHNODE_H
#define HASHNODE_H
#include "ML_hash.h"
template < typename T >
class HashNode
{
... // function prototype declarations
};
template< typename T >
void HashNode< T >::insert(int key, T* object)
{
...
int retVal = ML_hash(1, 3);
...
}
...
#endif
ML_hash.h:
#ifndef INC_ML_HASH
#define INC_ML_HASH
int ML_hash(int level, int key );
#endif
The error I am getting is:
g++ -o hash Hashtest.o:
Hashtest.o: In function `HashNode<int>::insert(int, int*)':
/home/adalal1/programs/class/final_project/HashNode.h:72: undefined reference to ' ML_hash(int, int)'
/home/adalal1/programs/class/final_project/HashNode.h:88: undefined reference to ` ML_hash(int, int)'
Hashtest.o: In function `HashNode<int>::explode()':
/home/adalal1/programs/class/final_project/HashNode.h:117: undefined reference to ` ML_hash(int, int)'
collect2: error: ld returned 1 exit status
What I don't understand is why the C++ compiler doesn't recognize the ML_hash function defined in ML_hash.cpp. I did include ML_hash.h in that cpp file. Could anyone provide insight into why this is happening?
EDIT:
I compile the code with a Makefile shown below:
C++ = g++
CFLAGS = -c -g
all: hash
hash: Hashtest.o
$(C++) -o hash Hashtest.o
clean:
rm -f *.o
%.o: %.cpp
$(C++) $(CFLAGS) $*.cpp
'ld returned 1 exit status' -> this is a linker error, not a compiler error.
Looks like you are running into this issue:
Linking files in g++
EDIT:
Either
g++ -o hash ML_hash.cpp HashNode.cpp HashTest.cpp
or
g++ -c ML_hash.cpp
g++ -c HashNode.cpp
g++ -c HashTest.cpp
g++ -o hash ML_hash.o HashNode.o HashTest.o
EDIT2 with OP edit:
I'm no makefile expert, but it looks like the 'hash:' target is just missing ML_hash.o and HashNode.cpp
hash: HashNode.o
ML_hash.o
Hashtest.o
$(C++) -o hash Hashtest.o ML_hash.o HashNode.o
Related
This question already has answers here:
Why the error of - calling the function before being declared, is not shown?
(3 answers)
Closed 4 years ago.
I have two files: test1.c, and test2.c, which contains the main() function.
test1.c:
#include <stdio.h> // printf() function declaration/prototype
// function definition
void say_hello() {
printf("\tHello, world!\n");
}
test2.c:
#include <stdio.h> // printf() function declaration/prototype
int main() {
printf("Inside main()\n");
say_hello();
return 0;
}
And this is my makefile:
a.out: test1.o test2.o
$(CXX) -o a.out test1.o test2.o
test1.o: test1.c
$(CXX) -c test1.c
test2.o: test2.c
$(CXX) -c test2.c
Now it should be clear where the problem lies: The main() function in test2.c calls say_hello() without declaring it!
I run the following command, to use the gcc compiler:
make CXX=gcc
I get this warning to the screen:
gcc -c test1.c
gcc -c test2.c
test2.c: In function ‘main’:
test2.c:16:3: warning: implicit declaration of function ‘say_hello’ [-Wimplicit-function-declaration]
say_hello();
^
gcc -o a.out test1.o test2.o
Although the *.o files got compiled and linked into the executable. That's weird. Imagine my surprise when I run the a.out file, and I see that main() successfully called say_hello(), a function which is not declared inside of main's translation unit, as if there were no issue at all! I reason that since say_hello() was not previously declared in test2.c, it should not allowed to be called by main() at all. Notice that I've added comments to the #include <stdio.h>. These preprocessor directives include the function declarations/prototypes, which extend their scope into the corresponding *.c files. That is why we are able to use them. No function declaration/prototype == no scope in that translation unit, or so I thought until now.
Then I compiled the above code as C++ code:
make CXX=g++
I get this error to the screen:
test2.c: In function ‘int main()’:
test2.c:16:13: error: ‘say_hello’ was not declared in this scope
say_hello();
^
makefile:18: recipe for target 'test2.o' failed
make: *** [test2.o] Error 1
g++ does what it's supposed to do, and stops the compilation process. But gcc did not do this! What's going on? Is it a perk of the C programming language? Is it an issue with the compiler?
Simple, because C allows undeclared functions to be called and C++ does not. Either way, gcc warns you and you may want to take warnings seriously.
I've created a source file that contains a number of data structures (maps, vector, array). Its header file is #included in the main-file.
The main file looks like this:
#include "reachability.h" //Where monkey() and vector<int> int_req are declared
main()
{
monkey(int_req); // Calling monkey(int_req) here is OK! Bar is visible
...
ifstream fp("foo.txt");
if(fp.is_open())
{
std::string line;
while( getline(fp,line) )
{
monkey(int_req); //'int_req' is an undefined reference!
}
}
}
And reachability.h
#ifndef REACHABILITY_H
#define REACHABILITY_H
extern std::vector<int> int_req;
void monkey(std::vector<int> feces);
#endif
And reachability.cc
std::vector<int> int_req;
void monkey(std::vector<int> thrown_obj)
{
... //Iteration and dereferencing of "thrown_obj"
}
I've accessed data structures that are declared in reachability.cc in a for-loop in the scope of main and that was fine. Something wonky is happening in this if-statement though.
Compiler Error:
lab1.o: In function `main':
/home/ubuntu/workspace/ECE597/Lab1/lab1.cc:105: undefined reference to `int_req'
collect2: error: ld returned 1 exit status
Edited: reachability.cc is included in compiliation:
elusivetau:~/XXXX/XXXX/XXXX $ g++ lab1.cc parser.cc gate.cc reachability.cc -o run
/tmp/ccJK4O9q.o: In function `main':
lab1.cc:(.text+0x489): undefined reference to `int_req'
collect2: error: ld returned 1 exit status
Edited: makefile for this program:
all: lab1.o parser.o gate.o reachability.o
g++ -g lab1.o parser.o gate.o reachability.o -o run
lab1.o: lab1.cc
g++ -g -c lab1.cc
parser.o: parser.cc
g++ -g -c parser.cc
gate.o: gate.cc
g++ -g -c gate.cc
reachability.o: reachability.cc
g++ -g -c reachability.cc
clean:
rm *o run
Whatever it is, you're not giving us the correct information.
I added includes and removed non-code to make this compile. And voila, it also links:
test.cpp:
#include "reachability.h" //Where monkey() and vector<int> int_req are declared
#include <string>
#include <iostream>
#include <fstream>
main()
{
monkey(int_req); // Calling monkey(int_req) here is OK! Bar is visible
std::ifstream fp("foo.txt");
if(fp.is_open())
{
std::string line;
while( getline(fp,line) )
{
monkey(int_req); //'int_req' is an undefined reference!
}
}
}
reachability.h:
#ifndef REACHABILITY_H
#define REACHABILITY_H
#include <vector>
extern std::vector<int> int_req;
void monkey(std::vector<int> feces);
#endif
reachability.cpp:
#include "reachability.h"
std::vector<int> int_req;
void monkey(std::vector<int> thrown_obj)
{
}
This compiles and links just fine. You are leading us on a wild goose chase by not bothering to create a mvce
lib.h:
#include <iostream>
namespace lib {
template <class T>
void f(T t)
{
std::cout << "lib f " << t << std::endl;
}
}
client.cpp:
#include "lib.h"
// explicit instantiation
template
void lib::f(char);
int main()
{
lib::f('x');
}
libmock.h:
#include <iostream>
#include "lib.h"
namespace lib {
template <>
void f(char c)
{
std::cout << "libmock f " << c << std::endl;
}
}
Makefile:
run: prod test
./prod
./test
prod: client.o
${CXX} -o $# $^
test: client.o libmock.o
${CXX} -o $# $^
clean:
-rm *.o prod test
Using GCC 4.3.2 (and also "IBM XL C/C++ for AIX, V11.1 (5724-X13)"), I get the results that I expect:
$ make
g++ -c -o client.o client.cpp
g++ -o prod client.o
g++ -c -o libmock.o libmock.cpp
g++ -o test client.o libmock.o
./prod
lib f x
./test
libmock f x
That is, I've injected new functionality into the client by linking it with an object that provides a more-specialized function template than the one offered by the library.
However, if I use "CC: Sun C++ 5.12 SunOS_sparc Patch 148506-14 2013/09/24", then I get this error:
$ CXX=CC make
CC -c -o client.o client.cpp
CC -o prod client.o
CC -c -o libmock.o libmock.cpp
CC -o test client.o libmock.o
ld: fatal: symbol 'void lib::f<char>(__type_0)' is multiply-defined:
(file client.o type=FUNC; file libmock.o type=FUNC);
Makefile:9: recipe for target 'test' failed
make: *** [test] Error 2
My solution must work with all three of these compilers. Am I just getting lucky with some undefined behavior in GCC and AIX? Are there some options I could pass to the Sun compiler to get this to work? Does what I'm trying to do show that I'm not fully understanding these template concepts? Enlighten me, please!
Your test binary that links libmock.o and client.o together violates the one definition rule (in the client translation unit it uses the default version and in the libmock translation unit it uses the specialized version) and thus both linker behaviors are ok.
I will continue thinking about alternatives but right now the only solutiion I can think of is to conditionally include libmock.h in client.cpp based on whether you're doing the mock test build or not.
So I wrote a small set of logging functions in the file cerus.h. The contents of that file can be seen below. It is being included in main.cpp, model.cpp, engine.cpp and camera.cpp. As can be seen, I have include guards so I'm not sure why I'm getting this error:
Output of $ make
jed#ArchPC:~/glPlayground$ make
g++ -std=c++11 -c model.cpp -o bin/model.o
g++ -std=c++11 -c tiny_obj_loader.cc -o bin/tinyobj.o
g++ -std=c++11 -c camera.cpp -o bin/camera.o
g++ -g -std=c++11 -o main bin/main.o bin/engine.o bin/tinyobj.o bin/model.o bin/camera.o -lGL -lGLU -lglut -lSOIL -lGLEW -lglfw
bin/engine.o: In function `LOG(char const*)':
engine.cpp:(.text+0x0): multiple definition of `LOG(char const*)'
bin/main.o:main.cpp:(.text+0x0): first defined here
bin/engine.o: In function `LOGERR(char const*)':
engine.cpp:(.text+0x3d): multiple definition of `LOGERR(char const*)'
bin/main.o:main.cpp:(.text+0x3d): first defined here
bin/model.o: In function `LOG(char const*)':
model.cpp:(.text+0x0): multiple definition of `LOG(char const*)'
bin/main.o:main.cpp:(.text+0x0): first defined here
bin/model.o: In function `LOGERR(char const*)':
model.cpp:(.text+0x3d): multiple definition of `LOGERR(char const*)'
bin/main.o:main.cpp:(.text+0x3d): first defined here
bin/camera.o: In function `LOG(char const*)':
camera.cpp:(.text+0x0): multiple definition of `LOG(char const*)'
bin/main.o:main.cpp:(.text+0x0): first defined here
bin/camera.o: In function `LOGERR(char const*)':
camera.cpp:(.text+0x3d): multiple definition of `LOGERR(char const*)'
bin/main.o:main.cpp:(.text+0x3d): first defined here
collect2: error: ld returned 1 exit status
Makefile:4: recipe for target 'main' failed
make: *** [main] Error 1
cerus.h
#ifndef CERUS_H
#define CERUS_H
#include <iostream>
//Need to add Windows and Mac Includes here
// Linux Include Statements
void LOG(const char* str){
std::cout << "[INFO]" << str << "\n";
}
void LOGERR(const char* str){
std::cout << "[ERROR]" << str << "\n";
}
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <GLFW/glfw3.h>
#endif
Makefile
all: main
main: bin/main.o bin/engine.o bin/model.o bin/tinyobj.o bin/camera.o cerus.h
g++ -g -std=c++11 -o main bin/main.o bin/engine.o bin/tinyobj.o bin/model.o bin/camera.o -lGL -lGLU -lglut -lSOIL -lGLEW -lglfw
bin/main.o: main.cpp cerus.h
g++ -std=c++11 -c main.cpp -o bin/main.o
bin/engine.o: engine.cpp engine.h cerus.h
g++ -std=c++11 -c engine.cpp -o bin/engine.o
bin/tinyobj.o: tiny_obj_loader.cc tiny_obj_loader.h cerus.h
g++ -std=c++11 -c tiny_obj_loader.cc -o bin/tinyobj.o
bin/model.o: model.cpp model.h cerus.h
g++ -std=c++11 -c model.cpp -o bin/model.o
bin/camera.o: camera.cpp camera.h cerus.h
g++ -std=c++11 -c camera.cpp -o bin/camera.o
clean:
rm -f bin/*.o main
If someone could explain to me why my guard isn't catching this, I would greatly appreciate the help.
EDIT: Fixed this issue by adding a file called cerus.cpp and defining my logging functions there instead of in cerus.h
This type of guard is to avoid things from being declared or defined in same translation unit.
It won't have any effect for multiple definition in different translation units (i.e. multiple source files).
In this case, you should move the definitions of functions LOG and LOGERR to another .cpp file, and put declarations of the functions in the header file.
Guards did nothing wrong, they just protect your declarations/inlines/templates.
It's the definitions that are real issue. If you have inline functions in your cpp, put them in header, same for templates. Do not include cpp files. Can't see much of your code but that is most of the cases.
This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
Closed 9 years ago.
It's the first time that I try to use templates in my functions but I can't seem to make them work. I defined my function in a file called ddc.hpp
#ifndef __DIGITAL_DOWN_CONVERTER_H__
#define __DIGITAL_DOWN_CONVERTER_H__
namespace ddc {
template <class T> void perform_resampling(std::vector<T> &, unsigned int, unsigned int);
}
#endif
and implemented it in ddc.cpp
#include "ddc.hpp"
template <class T>
void ddc::perform_resampling(std::vector<T> &data, unsigned int f1, unsigned int f2) {
// do stuff
}
and here's my main.cpp
#include "ddc.hpp"
int main() {
std::vector<float> v (100000);
ddc::perform_resampling(v, 1000, 10);
return 0;
}
Compiling with gcc (linux) I get the following error:
$ g++ -c ddc.cpp -o ddc.o -Wall -O3 -lm -m64
$ g++ -c main.cpp -o main.o -Wall -O3 -lm -m64
$ g++ ddc.o main.o -o ../bin/resampler
main.o: In function `main':
main.cpp:(.text.startup+0xed): undefined reference to `void ddc::perform_resampling<float>(std::vector<float, std::allocator<float> >&, unsigned int, unsigned int)'
collect2: ld returned 1 exit status
make: *** [../bin/HW_3] Error 1
Am I doing something wrong?
Template definitions need to go with declarations, so everything needs to be in the header file.
You need to put your template implementation in the header too.
You need to place the definition of the template function in a location that is visible to the code that uses it or use explicit template instantiation to ensure the code for the function is generated.
If you do not want to expose the implemention of perform_resampling you can still force the the compiler to explicitly generate the code for it. The following line when placed in ddc.cpp will instruct the compiler to generate code for perform_resampling taking a vector<float> as it's first parameter.
template void ddc::perform_resampling(std::vector<float> &data, unsigned int f1, unsigned int f2);