3

I have no idea why this is happening. The error I get is the following:

Error   2   error LNK2019: unresolved external symbol "public: int __thiscall Graphics::GLMatrix::getColumnSize(void)" (?getColumnSize@GLMatrix@Graphics@@QAEHXZ) referenced in function _SDL_main  C:\Users\holland\Documents\code\c++\projects\OpenGL_01\OpenGL_01\main.obj
Error   3   error LNK2019: unresolved external symbol "public: int __thiscall Graphics::GLMatrix::getRowSize(void)" (?getRowSize@GLMatrix@Graphics@@QAEHXZ) referenced in function _SDL_main    C:\Users\holland\Documents\code\c++\projects\OpenGL_01\OpenGL_01\main.obj
Error   4   error LNK1120: 2 unresolved externals   C:\Users\holland\Documents\code\c++\projects\OpenGL_01\Debug\OpenGL_01.exe  

What isn't being linked is a getRowSize() and getColumnSize() function from my Matrix class. What am I doing wrong?

So, what am I doing wrong here? I've been searching...searching every which way.

The Code

Header file:

#ifndef GLMATRIX_H
#define GLMATRIX_H

#pragma once

#include <array>


namespace Graphics {

    class GLMatrix
    {
    public:
        GLMatrix(int sizeX, int sizeY);
        ~GLMatrix();
        void allocMatrix();
        void addColumnI(int row, int column, long item);
        void revertRowsByColumns(); /*changes the formula of r * c to c * r*/
        GLMatrix &operator *(float scalar); /*multiply by scalar*/
        inline int getRowSize();
        inline int getColumnSize();
    private:
        int _sizeX, _sizeY;
        long** _pArray;
    };

}

#endif //GLMATRIX_H

Source:

#include "GLMatrix.h"

namespace Graphics {

    GLMatrix::GLMatrix(int sizeX, int sizeY)
    {
        _sizeX = sizeX; 
        _sizeY = sizeY;
    }

    GLMatrix::~GLMatrix()
    {
        delete _pArray;
    }

    void GLMatrix::addColumnI(int row, int column, long item) {

    }

    inline int GLMatrix::getRowSize() {
        return _sizeX;
    }

    inline int GLMatrix::getColumnSize() {
        return _sizeY;
    }

    void GLMatrix::allocMatrix() {

        _pArray = new long*[_sizeX];

        for (int i = 0; i < _sizeX; ++i) {          
            _pArray[i] = new long[_sizeY];
        }

    }

    void GLMatrix::revertRowsByColumns() {

        long** columns = new long*[_sizeY];

        for (int col = 0; col < _sizeY; ++col) {
            columns[col] = new long[_sizeX];
            memmove(
                columns + col, 
                _pArray[_sizeX - col], 
                sizeof(_sizeX) - sizeof(col)
            );
        }   
    }

}

Main:

#include <SDL.h>
#include "GLMatrix.h"


int main(int argc, char* argv[]) {

    //SDL_Init(SDL_INIT_EVERYTHING);

    //matrix test

    Graphics::GLMatrix* matrix = new Graphics::GLMatrix(3, 3);

    int num_rows = matrix->getRowSize();
    int num_columns = matrix->getColumnSize();
    for (int row = 0; row < num_rows; ++row) {

    }

    //SDL_Quit();

    delete matrix;

    return 0;
}
A.R.
  • 15,405
  • 19
  • 77
  • 123
zeboidlund
  • 9,731
  • 31
  • 118
  • 180

4 Answers4

2

The common knowledge that inline function MUST be declared in the header file is no longer true. Since several years, most compilers implement a feature called Link Time Optimization (gcc) or Link Time Code Generation (VC) that save important information about inline functions (among others) allowing the linker to look at all the object files as "one big happy translation unit". Thus the linker can inline functions you put in a cpp file.

Relevant links: http://msdn.microsoft.com/en-us/library/xbf3tbeh.aspx http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html (search for -flto)

Edit: It was apparently not understood from what I wrote earlier. This feature was not designed to save lazy programmers from declaring inline functions in each translation unit. But it does give you this byproduct that can cause problems. Do declare inline functions in the header file.

A.R.
  • 15,405
  • 19
  • 77
  • 123
Vladimir
  • 69
  • 4
  • 3
    -1. You're claim that inline functions are not required to be in the header is right but if they are not in the header they need to be in every translation unit. Link-time optimization/code generation is not the way to obtain a definition for every translation unit, since it makes the standard-conformity of the program depedant on compiler options rather than the code. Inline functions need to be defined in every translation unit either by defining them seperately or by inline definition in the header file. – Pixelchemist Jul 17 '13 at 06:40
  • Thanks for the clarification! But I thing you are confusing Must and Should. We Should not rely on compiler options in this case, but it does work and requires you to be aware of this feature (that's how I came across this issue originaly). – Vladimir Jan 15 '14 at 13:51
  • 1
    No I am not confusing must and should but I think you do. We MUST NOT rely on the link time optimization for a valid function definition to be present. A program missing the definition of an inline function in a translation unit (it is used in) is non-conforming. So even if LTO/LTCG makes it work it is still wrong since you possibly won't even be able to compile the program without LTO/LTCG if you rely on this behaviour. So don't do it. – Pixelchemist Jan 15 '14 at 14:44
  • Ok, fair enough :) I never do it of course, and that's what I wrote in the edit to the post. – Vladimir Jan 15 '14 at 15:05
1

1. A translation unit is a single source file ("Source" and "Main" in your case) together with the included files.

2. Inline functions

C++11, §3.2/3

"[...] An inline function shall be defined in every translation unit in which it is odr-used. [...]"

§ 7.1.2/4

An inline function shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case (3.2). [...]

Your "Source" translation unit has a definition of getRowSize and getColumnSize. Your "Main" translation unit has not! This is undefined behaviour since the compiler is not required to check for that.

LTCG/LTO

Parts of Vladimir's answer are correct. There is link time optimization (or link time code generation). But it doesn't serve the purpose of helping lazy programmers to save function-definitions for inline functions.

LTCG/LTO is done to make all functions visible to the compiler at once, which in turn may decide to inline function which aren't visible to it under normal circumstances. Functions declared inline must per definition visible to the compiler at every point they are used at. Therefore, LTCG/LTO is not needed for (and should not be abused for the purpose of) solving missing inline link errors.


Just to make things clear: I'm well aware of the fact that this is an old question but it isn't answerd correctly yet and I just fell over it.
Pixelchemist
  • 24,090
  • 7
  • 47
  • 71
1

I was also facing with this linking error issue for the inline functions in the library for Visual Studio 2019. I set the property "Remove Unreferenced Code and data" to "No" in the tab Language of C/C++ properties and resolved the linking error.

Sumedha
  • 11
  • 1
1

Inline functions must be defined in header files. (More specifically, the definition must be visible in every translation unit in which it is used.)

Raymond Chen
  • 44,448
  • 11
  • 96
  • 135
  • 2
    Informally, a translation unit is a single C++ file and all the files it #includes – Raymond Chen Dec 23 '11 at 04:31
  • For dummies: you can NEVER NEVER NEVER put an inline function in one .cpp file and call it from another .cpp file no matter what header files you have. Inline functions must either be in the same .cpp file as they are called or be somewhere in an .h file. If you don't do this, you're gonna spend 4 hours debugging linker errors without success. Make sure you read http://www.parashift.com/c++-faq-lite/inline-functions.html – Hossein Jun 05 '12 at 13:16