I am building on an M1 Pro Mac (CLion) and am getting the following error while compiling:
FAILED: tree
: && /Library/Developer/CommandLineTools/usr/bin/c -g -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.0.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/tree.dir/main.cpp.o CMakeFiles/tree.dir/Node.cpp.o CMakeFiles/tree.dir/Tree.cpp.o CMakeFiles/tree.dir/Unit.cpp.o -o tree && :
Undefined symbols for architecture arm64:
"Node<Unit>::addRoot(Unit)", referenced from:
_main in main.cpp.o
"Node<Unit>::Node()", referenced from:
_main in main.cpp.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.
I have three classes, Node
, Tree
and Unit
. Most of the posts I have come across suggests that one of the files are not included in the project (eg Undefined symbols for architecture arm64 using g compiler). I doubt this is the issue, since they all appear to be included in the compiler line.
My CMakeLists.txt
looks like this:
cmake_minimum_required(VERSION 3.21)
project(tree)
set(CMAKE_CXX_STANDARD 14)
add_executable(tree main.cpp Node.cpp Node.h Tree.cpp Tree.h Unit.cpp Unit.h)
I am adding the header file for the Unit
class since this seems to be the one that offends the compiler, however I doubt this is where the issue lies. Guessing it is some make file setup?
#include <iostream>
#include <vector>
using namespace std;
template <class T>
class Node {
weak_ptr<Node> parent;
vector<shared_ptr<shared_ptr<Node>>> children;
T data;
public:
Node();
Node(weak_ptr<Node> parent, T &data);
void addRoot(T node_data);
};
And at risk of being too verbose in this post, here is the CMakeError.log
output:
Compiling the C compiler identification source file "CMakeCCompilerId.c" failed.
Compiler: /Library/Developer/CommandLineTools/usr/bin/cc
Build flags:
Id flags:
The output was:
1
ld: library not found for -lSystem
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Compiling the CXX compiler identification source file "CMakeCXXCompilerId.cpp" failed.
Compiler: /Library/Developer/CommandLineTools/usr/bin/c
Build flags:
Id flags:
The output was:
1
ld: library not found for -lc
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Output if I run c main.cpp Node.cpp Tree.cpp Unit.cpp
:
In file included from main.cpp:3:
./Node.h:16:38: error: a space is required between consecutive right angle brackets (use '> >')
vector<shared_ptr<shared_ptr<Node>>> children;
^~
> >
main.cpp:7:5: warning: 'auto' type specifier is a C 11 extension [-Wc 11-extensions]
auto unit = std::make_unique<Unit>(1, "Test Unit");
^
main.cpp:7:22: error: no member named 'make_unique' in namespace 'std'
auto unit = std::make_unique<Unit>(1, "Test Unit");
~~~~~^
main.cpp:7:34: error: 'Unit' does not refer to a value
auto unit = std::make_unique<Unit>(1, "Test Unit");
^
./Unit.h:11:7: note: declared here
class Unit {
^
main.cpp:7:40: warning: expression result unused [-Wunused-value]
auto unit = std::make_unique<Unit>(1, "Test Unit");
^
main.cpp:8:20: error: expected ';' at end of declaration
Node<Unit> node {};
^
;
2 warnings and 4 errors generated.
In file included from Node.cpp:5:
./Node.h:16:38: error: a space is required between consecutive right angle brackets (use '> >')
vector<shared_ptr<shared_ptr<Node>>> children;
^~
> >
1 error generated.
I am a relative new comer to C so am finding this issue somewhat troublesome to track down.
CodePudding user response:
It looks like you are having problems with templates. In templated classes you have to either place the implementation of the methods all in the header file or make explicit template instantiations of the concrete classes in the cpp body that defines it.
It's easier to see with an example. In the following case I'm adding the body of the constructor in the header so I dont' have to do anything else
/////////////////////////////////////////
// In Node.h
/////////////////////////////////////////
template< typename T >
struct Node {
Node();
};
template< typename T >
Node<T>::Node() {
}
/////////////////////////////////////////
// In Node.cpp
/////////////////////////////////////////
// (nothing)
/////////////////////////////////////////
// Int main.cpp
/////////////////////////////////////////
#include "Node.h"
int main() {
Node<int> n;
}
In the second case I will make an explicit template instantiation of the concrete class Node<int>
in the body of Node.cpp such that I don't need to expose the implementation of the templated classes.
/////////////////////////////////////////
// In Node.h
/////////////////////////////////////////
template< typename T >
struct Node {
Node();
};
/////////////////////////////////////////
// In Node.cpp
/////////////////////////////////////////
template< typename T >
Node<T>::Node() {}
// Explicit initialization
template struct Node<int>;
/////////////////////////////////////////
// Int main.cpp
/////////////////////////////////////////
#include "Node.h"
int main() {
Node<int> n;
}
I prefer to use the 2nd method since it lowers build times significantly.