0

g++ fails to compile the below files at the linking stage with multiple undefined reference to [function] errors.

I am learning C++11 using a textbook. All of the other questions and answers on this topic involve makefiles or templates, which I did not use here.

g++ version is 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04).

main.cpp:

#include "Screen.h"

// #include <string>
using std::string;

// #include <iostream>
using std::cout;
using std::endl;

int main()
{
    Screen myScreen(5, 5, 'X');
    myScreen.move(4,0).set('#').display(cout);
    cout << endl;
    myScreen.display(cout);
    cout << endl;

    return 0;
}

Screen.h:

#ifndef SCREEN_H
#define SCREEN_H

#include <string>
#include <iostream>

class Screen
{
public:
    // typedefs
    using pos = std::string::size_type;

    // constructors
    Screen()
        = default;
    Screen(pos ht, pos wd):
        height  (ht),
        width   (wd),
        contents(ht * wd, ' ')
    {}
    Screen(pos ht, pos wd, char c):
        height  (ht),
        width   (wd),
        contents(ht * wd, c)
    {}

    // public member functions
              char get    () const;
    inline    char get    (pos, pos) const;
           Screen& move   (pos, pos);
           Screen& set    (char);
           Screen& set    (pos, pos, char);
           Screen& display(std::ostream&);
     const Screen& display(std::ostream&) const;

private:
    // class variables
            pos cursor   = 0,
                height   = 0,
                width    = 0;
    std::string contents    ;

    // private member functions
    void do_display(std::ostream&) const;
};
#endif

Screen.cpp:

#include "Screen.h"

// #include <string>
using std::string;

// #include <iostream>

/*
 ####  #   # ####  #     ###  ###
 #   # #   # #   # #      #  #   #
 ####  #   # ####  #      #  #
 #     #   # #   # #      #  #   #
 #      ###  ####  ##### ###  ###

 #    # ##### #    # ####  ##### ####   ####
 ##  ## #     ##  ## #   # #     #   # #
 # ## # ####  # ## # ####  ####  ####   ###
 #    # #     #    # #   # #     #  #      #
 #    # ##### #    # ####  ##### #   # ####
*/

char Screen::get() const
    {return contents[cursor];}

/*inline*/ char Screen::get (pos r, pos c) const
{
    pos row = r * width;
    return contents[row + c];
};

inline Screen& Screen::move(pos r, pos c)
{
    pos row = r * width;
    cursor = row + c;
    return *this;
}

inline Screen& Screen::set(char c)
{
    contents[cursor] = c;
    return *this;
}

inline Screen& Screen::set(pos row, pos col, char ch)
{
    contents[(row * width) + col] = ch;
    return *this;
}

Screen& Screen::display(std::ostream& os)
{
    do_display(os);
    return *this;
}

const Screen& Screen::display(std::ostream& os) const
{
    do_display(os);
    return *this;
}

/*
 ####  ####  ### #   #  ###  #####  #####
 #   # #   #  #  #   # #   #   #    #
 ####  ####   #  #   # #####   #    ####
 #     #  #   #   # #  #   #   #    #
 #     #   # ###   #   #   #   #    #####

 #    # ##### #    # ####  ##### ####   ####
 ##  ## #     ##  ## #   # #     #   # #
 # ## # ####  # ## # ####  ####  ####   ###
 #    # #     #    # #   # #     #  #      #
 #    # ##### #    # ####  ##### #   # ####
*/

inline void Screen::do_display(std::ostream& os) const
    {os << contents;}

I followed everything in the book, so I'd expect g++ to compile the files with no errors. But i'm getting these errors in console:

$ g++ -o program main.cpp Screen.cpp -std=c++11
/tmp/ccBELGiY.o: In function `main':
main.cpp:(.text+0x45): undefined reference to `Screen::move(unsigned long, unsigned long)'
main.cpp:(.text+0x52): undefined reference to `Screen::set(char)'
collect2: error: ld returned 1 exit status
dan9er
  • 369
  • 2
  • 17
  • How does your C++ book describe the `inline` keyword? – Sam Varshavchik Jan 20 '19 at 19:16
  • @SamVarshavchik I copied the relevant section in the book into Markdown: https://hastebin.com/enoqadiruh.md – dan9er Jan 20 '19 at 19:55
  • 1
    Reread the "a function specified as inline (usually) is expanded "in line" at each call" part. Now, think about what it means. If the inline function is (usually) expanded at each call, but the function is defined in one translation unit, by itself, it means that it's not really available to be called from other translation units, doesn't it? So, what exactly is unclear, about this link failure? – Sam Varshavchik Jan 20 '19 at 20:39
  • @SamVarshavchik What is a "translation unit"? Sorry, still learning. – dan9er Jan 21 '19 at 18:47
  • 1
    It means a single C++ source file, that's individually compiled. – Sam Varshavchik Jan 21 '19 at 18:54
  • @SamVarshavchik So you're saying `inline` breaks functions that return `*this`? – dan9er Jan 21 '19 at 19:04
  • No, `inline` doesn't have anything to do, whatsoever, with what a function returns. – Sam Varshavchik Jan 21 '19 at 19:34
  • 1
    ...and the relevant answer is [Functions or class-methods are defined in source files with the inline specifier](https://stackoverflow.com/a/54296817/1362568) – Mike Kinghan Jan 21 '19 at 19:47
  • @MikeKinghan Just tried to add explicit `inline` tags to my function declarations in Screen.h, same issue now with warnings about `inline function [...] used but never defined`. Are you saying I MUST define the `inline`s in-class? – dan9er Jan 23 '19 at 18:35
  • 1
    @dan9er See **foo.h (2)** in [the linked answer](https://stackoverflow.com/a/54296817/1362568). You have those two alternatives. A function or method *cannot be inlined* in a translation unit that does not contain its definition. If you *declare* it `inline` in a translation unit, with no definition, then you get the compilation error you've just observed. – Mike Kinghan Jan 23 '19 at 18:55

1 Answers1

1

As Mike Kinghan pointed out, this was already answered here: https://stackoverflow.com/a/54296817/1362568

Basically, the problem was I implemented my inline functions in my Screen.cpp file instead of my .h file. Since inlines are expanded "in-line" and are not "real" functions, the linker couldn't find a "real" function to define the ones I declared in Screen.h.

This was my mistake, caused by not following my textbook to the letter since my mentor told me to always put definitions in a separate .cpp file.

I fixed this by defining my inlines in my .h file.

dan9er
  • 369
  • 2
  • 17