#Linking Error CMake

197 messages · Page 1 of 1 (latest)

hot heath
#

i have problems with cmake and compiling my project with it.
my structure:

the error:

MSBuild version 17.8.3+195e7f5a3 for .NET Framework

  1>Checking Build System
  Building Custom Rule C:/Users/Flint/Desktop/carray/CMakeLists.txt
  constructor.cpp
  main.cpp
  Generating Code...
main.obj : error LNK2019: unresolved external symbol "public: __cdecl CArray1<float>::CArray1<float>(void)" (??0?$CArray1@M@@QEAA@XZ) referenced in function main [C:\Users\Flint\Desktop\carray\build\main.vcxproj]
C:\Users\Flint\Desktop\carray\build\Debug\main.exe : fatal error LNK1120: 1 unresolved externals [C:\Users\Flint\Desktop\carray\build\main.vcxproj]

C:.
│ CMakeLists.txt
│ main.cpp

└───carray
└───carray1
│ carray1.hpp

└───property
└───constructor
constructor.cpp

main.cpp:

#include "carray/carray1/carray1.hpp"

int main() {
    CArray1<float> x = CArray1<float>();
    return 0;
}

carray1.hpp:

#ifndef CARRAY1_HPP
#define CARRAY1_HPP

template<typename T>
class CArray1 {
private:
    T* _array;
    size_t _size;

public:
    CArray1();

};

#endif

constructor.cpp:

#include "../../carray1.hpp"

template<typename T>
CArray1<T>::CArray1() {
    this->_array = new T();
    this->_size = 0;
}

cmakelists.txt:

cmake_minimum_required(VERSION 3.12)

project(my_project)

file(GLOB_RECURSE all_files "*.cpp" EXCLUDE "build")

list(FILTER all_files EXCLUDE REGEX "build")

message(STATUS "Found source files:")
foreach(file IN LISTS all_files)
    message(STATUS "  ${file}")
endforeach(file)

add_executable(main ${all_files})
modest daggerBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

queen tapir
#

@hot heath You can't define templated functions in source files, they need to be in header files.

hot heath
#

what does that mean?

queen tapir
#

Say we didn't have a template, we could write a class like this

#
// header.hpp
class printer {
public:
  printer(int number);
};```
#
// header.cpp
printer::printer(int number) {
  std::cout << number << std::endl;
}```
#

right?

hot heath
#

yes

queen tapir
#

the printer(int number); is a function declaration

#

You're saying "this function exists"

#

Then the cpp printer::printer(int number) { std::cout << number << std::endl; } is saying "This is what that function actually is"

hot heath
#

but i have it as constructor.. isnt it "the same"

queen tapir
#

We will get there

hot heath
#

ah ok

queen tapir
#

If it helps, you can imagine declarations as something on a menu

#

And definitions are the thing itself

hot heath
#

ok

queen tapir
#

So why does this make templates fuck up?

#

Lets get into it

#

Templates are literally templates for the compiler

#

The way that they work is like this

template <typename T>
T add(T a, T, b) {
  return a + b;
}```
#

This is not a function, it's a template function.

hot heath
#

yes

queen tapir
#

While they have similar names, they're treated very differently by the compiler.

#

When the compiler sees template <>... anything

#

(template class, function, variable, etc...)

#

It knows "ok, this is not code, but instructions on how to MAKE code"

#

So if you call
std::cout << add<int>(5, 10) << std::endl

#

The compiler will actually create

template <>
int add(int a, int b) {
  return a + b;
}```
#

So, do you see how the compiler needs the definition?

hot heath
#

yes i understand that

queen tapir
#

Because just saying "this template function exists" isn't enough to actually generate it

hot heath
#

so i have to define what variables can go into?

queen tapir
#

It's like asking a chef to copy a food just by looking at the menu

#

You just can't, you need to see it for yourself

#

Now, you will act as the compiler ok?

hot heath
#

ok continue.. its really helpful

#

yes

queen tapir
#

If I ask you to process this

template <typename T>
T subtract(T a, T, b) {
  return a - b;
}``` for `subtract<float>(40.4f, 29.3f);`
#

What would you make it? (Hint: look above at the add example)

hot heath
#

float subtract(float a, float b) { return a - b }

queen tapir
#

Exactly

#

Now, how would you do it if I gave you this

#
template <typename T>
T do_something_with_numbers(T a, T b);
#

remember, you have to generate complete code, an entire function

#

if I tell you to replace T with float here

#

You can't create the function, because you don't know it

hot heath
#

yeah i see

queen tapir
#

To be very technical, all template code needs to be visible from within the translation unit that's compiling it *

#

*This is actually changing with the introduction of modules in C++. I don't entirely know how that works from a language standpoint. So while I can't fully explain, just know that it's changing.

hot heath
#

yes

queen tapir
#

Does that help answer your question?

hot heath
#

yeah kinda.. but how should i write my code then?

queen tapir
#

Here's the thing, I'm not going to tell you exactly what to do to fix it.

#

You don't learn anything that way

#

Read through the conversation, try to really understand everything

#

If you have questions, ask me and I'll help you out

hot heath
#

like one way is to write everything for float, double, .. but thats not the right way xD

queen tapir
#

You should be able to fix it yourself once you understand this conversation.

hot heath
#

mhh im still confused.. i think im too familiar to the rust apporach

queen tapir
#

Oh, you know rust?

hot heath
#

where i can define what T can be and then just "replaces" T to the specific type

#

yes

queen tapir
#

How well?

hot heath
#

pretty well

queen tapir
#

oh my lord

#

im so sorry lol

#

I thought you were a complete beginner (That's just what I assume)

hot heath
#

no it was ok

queen tapir
#

no shit the way I'm talking doesn't help

#

ok so

#

I'll try to explainit from a rust approach

#

What Im going to say isn't going to be accurate, you're just going to have to hang in with me while I do this

hot heath
#

actually liked it to see it from a different perspective tbh

#

yes

queen tapir
#

Rust and C++ templates are practically exactly the same in terms of usage

#

The big thing is that, in C++, we have declarations and definitions

hot heath
#

oh ok that sounds good

queen tapir
#

Rust "hides" all of the "ugly" internal stuff of compiling

#

How comfortable are you with compilers? and object files?

hot heath
#

not too much

queen tapir
#

ok, ill keep it light

#

To make compilation fast, C++ compiles each file by itself

hot heath
#

yes

queen tapir
#

Each file is compiled "specially", where it's not source code, but it's not an executable either.

#

That's what happens to each .cpp file

#

it becomes a .o file

#

This is where the linker comes in

#

it takes all of your .o files, and links them together

#

Now, this is where its going to get a bit wonky

hot heath
#

yeah i know.. thats why ive added them with cmake

queen tapir
#

.hpp files dissapear when compiling

#

They literally get copy pasted

#

Notice that if you make a function in a header file like

int add(int a, int b);```
#

but you never create a .cpp file for it

#

You get a "Symbol missing error" during link time

hot heath
#

yes i know

queen tapir
#

That's because the linker is like

#

"ayo, this .o file wants a function called int add(int, int). but I can't find that anywhere"

#

Following so far?

hot heath
#

yes

queen tapir
#

This is where we'll talk about templates

#

Remember how .hpp files get copy pasted?

hot heath
#

yes

queen tapir
#

And then each .cpp file is compiled by itself

hot heath
#

yes

queen tapir
#

This is a simple example of your problem

#
// add.hpp
template <typename T>
T add(T a, T b);```
#
// add.cpp
#include "add.hpp"

template <typename T>
T add(T a, T b) {
  return a + b;
}```
#
// main.cpp 
#include "add.hpp"

int main() {
  std::cout << add<int>(5, 10) << std::endl;
}```
#

If you try to compile this, you'll get the same error

#

now the question is why

#

let's "compile" these files to find out

#
// add.cpp
template <typename T> // include has been copy pasted into file
T add(T a, T b);

template <typename T>
T add(T a, T b) {
  return a + b;
}```
#
// main.cpp 
template <typename T> // include has been copy pasted into file
T add(T a, T b);

int main() {
  std::cout << add<int>(5, 10) << std::endl;
}```
#

So now we've ran the preprocessor

#

Now let's process the template

#
// add.cpp
// Since no one was using the template, the template code goes away
// main.cpp 
int add(int a, int b);

int main() {
  std::cout << add<int>(5, 10) << std::endl;
}```
#

Where is the cpp int add(int a, int b) { return a + b; }

#

See why it doesn't exist?

hot heath
#

gimme a second

#

yeah its in the add.cpp right?

#

wait u confused me.. like ur first explanation where the T doesnt know what it is "just a complex funktion" got me thinking and i added a specifier: template class CArray1<float>;
(where i thought by adding the <float> it knows that i need it)

#

so i treat it the opposite like rust does.. rust exclude the generic by adding trait requirements and i added includes

queen tapir
#

because no one asked for it

#

in main.cpp you ask for cpp template <typename T> T add(T a, T b); to be processed with T = int

hot heath
#

so hpp get copy pasted to its include "add.cpp" and the main.cpp so main.cpp doesnt have to ask for add.cpp and asks its own

queen tapir
#

main.cpp doesn't know add.cpp exists

#

so that's why it can't generate the function definition

#

it doesn't exist in the eyes of the main file

#

and the reason this isn't an issue without templates is that

#

the function exists in add.cpp, then the linker knows where to find it

#

but since no one asked for cpp template <typename T> T add(T a, T b) { return a + b; } to be processed with int in add.hpp

#

it doesn't exist

#

that's why to make this work

#

you dont have a add.cpp

#

you put everything in the header file

#

because now this is what compiling looks like

#
// add.hpp

template <typename T>
T add(T a, T b) {
  return a + b;
}```
hot heath
#

if i put it in the header file its getting "copy pasted" to the main.cpp bc its calling for it right?

queen tapir
#
// main.cpp
#include "add.hpp"

int main() {
  std::cout << add<int>(10, 4) << std::endl;
}```
queen tapir
hot heath
#

ah ok

queen tapir
#

after the template is "processed" with int

#
int add(int a, int b) {
  return a + b;
}

int main() {
  std::cout << add(10, 4) << std::endl;
}```
#

and now the compiler knows what int add(int a, int b); is supposed to do

hot heath
#

oh ok

queen tapir
#

sorry if there's a typo or two

#

i've typed everything in this chat

#

no copy paste ;-;

hot heath
#

no problem..

#

ur doing it great!

queen tapir
#

(i type at about 200wpm, so that helps)

hot heath
#

thats cool! xD

#

ok but is it possible to split the header into cpp with those templates?
and what does the template class CArray1<float>; at the end of the file do bc now it compiles (but i didnt test the code yet)

queen tapir
#

no, you can't split template code into headers and cpp files

#

if you have a template, you always need to have the function declartion and definition together*

#

*technically you don't, and there's some situations where you can take advantage of this, but you shouldn't worry about that for now

#

Maybe spoiling the answer will help it click

#

All you need to do

#
#ifndef CARRAY1_HPP
#define CARRAY1_HPP

template<typename T>
class CArray1 {
private:
    T* _array;
    size_t _size;

public:
    CArray1();

};

#endif```
```cpp
#include "../../carray1.hpp"

template<typename T>
CArray1<T>::CArray1() {
    this->_array = new T();
    this->_size = 0;
}```
hot heath
#

can i split the class into different header files?
bc everything in one big header isnt that good (all i heard of)

queen tapir
#

turn the header file into

hot heath
#

what?

queen tapir
#

sorry

#

heard a door open

#

thought someone was trying to break in / rob me

hot heath
#

oh no problem

queen tapir
#

so I got my katana out

#

had to do a quick sweep lel

hot heath
#

xD

hot heath
#

always keep the katana at ur desk

queen tapir
#

everything is on bigg ass header

queen tapir
hot heath
#

ahh nice xD

#

why not

queen tapir
#

You put everything into one big ass header

hot heath
#

i have salt and chilli spice next to me xD

queen tapir
#

It is typically said to be bad to do

#

but not for templates

#

that's how you should do it

hot heath
#

ok but the header is only for template.. any other class would work with hpp-cpp files

queen tapir
#

putting everything in a header is only for templates yes

hot heath
#

ok but i think i actually understand why now

#

that helped a lot!

queen tapir
#

np

#

glad I could help

hot heath
#

thank you so much!

#

ok then im marking it as solved!

#

:)

#

!solved