0

I am learning Modern C++ form a course by University of Bonn. I have declared a class in a grass.h file and its implementation in a corresponding grass.cpp file. Similarly in another tools.h-tools.cpp declaration-implementation pair is a function that takes in a reference to the class object defined above. And finally a main.cpp file initialises a Grass object from first pair and passes it to the function from the second pair. I am able to compile main.cpp and get the desired output from these command line instructions:

c++ -std=c++11 -g -c grass.cpp -o grass.o
c++ -std=c++11 -g -c tools.cpp -o tools.o
c++ -std=c++11 -g main.cpp grass.o tools.o -o main

but unable to do so with CMake. I do not know what the command line instructions are called (linking and compiling?).

Here is the code example "grass.h":

#include <string>
#pragma once

class Grass
{
private:
    int green_;
    std::string name_;
public:
    Grass(int green, std::string name): green_{green}, name_{name} {}

    int get_green() const;                        // getter functions for 
    const std::string& get_green_name() const;    // both private members
};

The functions just return the values and are implemented in "grass.cpp".

Then I have a "tools.h":

#include <iostream>
#include "grass.h"

#pragma once

void PrintGrass(Grass& grObj);

And this function just prints values from the getter functions of Grass.

Finally, the main.cpp contains:

int main ()
{
    Grass grObj{10, "ABC"};
    PrintGrass(grObj);
    cout << endl;
    return 0;
}

The CMake error occurs at 100% Linking CXX executable main. The error is in tools.cpp: undefined reference to `Grass::get_green_name[abi:cxx11]()' and undefined reference to `Grass::get_green()'. This means that the the program is unable to find to find the definition of these function, right? The CMake file looks like this:

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0")
add_library(grass grass.cpp)
add_library(tools tools.cpp)
add_executable(main main.cpp)
target_link_libraries(main grass tools)

How do I make this work with CMake? Thank you very much for your help.

  • 1
    It is not a CMake error. Its a linker error complaining of an undefined reference. It means you have declared `get_green()` and `get_green_name()` inside grass.h but you have not implemented them in your grass.cpp file. – MarKS Sep 07 '20 at 10:16
  • 1
    I don't know much cmake, but I guess `target_link_libraries` is just as fragile as regular `-l` flag in gcc and it will discard libraries that are not used at the time. Try `target_link_libraries(main tools grass)` instead. – Yksisarvinen Sep 07 '20 at 10:20
  • 3
    Or just don't use libraries, I'm not sure why did you choose this route. `add_executable(main main.cpp grass.cpp tools.cpp)` would work. – Yksisarvinen Sep 07 '20 at 10:26
  • These functions are defined, OP in first paragraph says they are able to link with the first snippet (using gcc directly), but not with CMake. The issue is with CMakeLists.txt – Yksisarvinen Sep 07 '20 at 10:36
  • 7
    Your working version doesn't build libraries and link with them. Unless you really intend to build a library for each file, you only need the `set` lines and `add_executable(main main.cpp grass.cpp tools.cpp)`. – molbdnilo Sep 07 '20 at 10:44
  • Everything looks OK, I cannot reproduce your problem. Most likely the issue is in your directory structure and the way you invoke CMake, or something similar to [this](https://stackoverflow.com/questions/55406770/gcc-undefined-references-with-abicxx11). Post the full code (also the implementation in cpp files), your project structure, and the output of `VERBOSE=1 make` – pptaszni Sep 07 '20 at 13:01
  • Thank you for all your ideas. As @Yksisarvinen mentioned, I have implemented those functions in grass.cpp. I do not understand how, but @Yksisarvinen 's idea worked. By writing it as ```target_link_libraries(main tools grass) ``` instead of the original ```target_link_libraries(main grass tools) ``` worked. – Shrinivas Iyengar Sep 07 '20 at 13:54
  • I would love to know why this works. Is it executing them in order they have been written? If so, is it because in the original file ```main``` calls function from ```tools``` in Line 4 first, and then ```tools``` uses an object of ```grass```. And so because it is in that order, it needs them to be written in that order? – Shrinivas Iyengar Sep 07 '20 at 14:02
  • [Why does the order in which libraries are linked sometimes cause errors in GCC?](https://stackoverflow.com/questions/45135/why-does-the-order-in-which-libraries-are-linked-sometimes-cause-errors-in-gcc) Linker is a lazy tool.It goes through libraries one by one and if a library doesn't contain any symbols that are currently missing, it will discard the whole library and forget it existed. Libraries are a fragile thing, and you need to make sure the libraries without dependencies are always last. When calling `gcc` directly, you never used any libraries, so you wouldn't encounter this problem. – Yksisarvinen Sep 07 '20 at 14:11
  • I would like to mention that @Yksisarvinen and @molbdnilo 's ```add_executable(main main.cpp grass.cpp tools.cpp)``` works as well. As to why I did the extra steps instead of just this one line, is because I learnt it that way from the guy teaching it. I am new to this and exploring more. – Shrinivas Iyengar Sep 07 '20 at 14:34
  • 1
    @Yksisarvinen CMake is able to solve linking ordering problem, when dependencies are known to it. In this example `tools` library depends on `grass` library, but it is not specified anywhere. Proper solution to this problem is to add another line `target_link_libraries(tools grass)`. PS. I suggest to use `target_link_libraries(x PUBLIC y)`, so CMake knows we are using modern syntax. – R2RT Sep 09 '20 at 12:49

2 Answers2

0

I had a similar problem solved by adding the include files too into add_library:

add_library(grass grass.cpp grass.h)

Try this and see if it works

Andrea Nisticò
  • 466
  • 2
  • 4
  • 11
0

I will to summarise the comments section and based on @pptaszni's advice put the entire code and the verbose error here:
The entire code in order of dependency:

  1. grass.h and grass.cpp:
#include <string>
#pragma once

class Grass
{
public:
    Grass(int green, std::string name): green_{green}, name_{name} {}

    int get_green ();
    std::string get_green_name ();
private:
    int green_ = 100;
    std::string name_ = "Joe";
};

and

#include "grass.h"

Grass::Grass(int green, std::string name): green_{green}, name_{name}{}

int Grass::get_green() {
    return green_;  
}

std::string Grass::get_green_name() { 
    return name_;
}
  1. tools.h and tools.cpp:
#include <iostream>
#include "grass.h"

#pragma once

void PrintGrass(Grass& grObj);

and

#include <iostream>
#include "grass.h"
#include "tools.h"

void PrintGrass(Grass& grObj)
{
    std::cout << grObj.get_green_name() << ", " << grObj.get_green() << " ";
}
  1. main.cpp
#include <iostream>
#include "grass.h"
#include "tools.h"
#include <string>

using std::string;
using std::cout;
using std::endl;


int main ()
{
    Grass grObj{10, "Jane"};
    PrintGrass(grObj);
    cout << endl;
    return 0;
}
  1. CMakeLists.txt
project(Grasses)
cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0")
add_library(grass grass.cpp)
add_library(tools tools.cpp)
add_executable(main main.cpp)
target_link_libraries(main grass tools)

The error output of make VERBOSE=1 being verbose, has been pasted in a Github Gist, so as to not make this post longer than it already is.

Summary
What worked? Writing

target_link_libraries(main tools grass)

instead of

target_link_libraries(main grass tools).

The take away message from the explanation given by @Yksisarvinen is to always place libraries without any dependencies at the very end. This is because the compiler might take a look at such libraries and discard them, if it does not find any use for it at the moment of processing it.

Another thing that works is just using add_executable(main main.cpp grass.cpp tools.cpp) and not adding libraries and then linking them. This was suggested by @molbdnilo and @Yksisarvinen.

Thank you all for helping me in understanding C++ a bit better.

  • True issue here is that the `tools` library depends on `grass` library, but it is not specified anywhere. Proper solution to this problem is to add another line `target_link_libraries(tools grass)` – R2RT Sep 09 '20 at 12:51