Huy Le
Huy Le

Reputation: 1738

C++ template class header-source separation linking error when following tutorial

----------------
// fishers.h
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <math.h>
#include <cstring>

namespace tsfresh 
{

template<typename T=double>
class FisherTest {
static_assert(std::is_floating_point_v<T>, "FisherTest template T must be float or double");

public:        
    FisherTest();

    T pvalue(int a, int b, int c, int d);

private:    
    T pValueLog(int a, int b, int c, int d);
};

}

// fishers.cpp
#include "fishers.h"

namespace tsfresh 
{    
    template<typename T>
    FisherTest<T>::FisherTest() {        
    }

    template<typename T>
    T FisherTest<T>::pvalue(int a, int b, int c, int d) {
        return pValueLog(a,b,c,d);
    }

    template<typename T>
    T FisherTest<T>::pValueLog(int a, int b, int c, int d) {
        return 0;
    }
}

//---
// https://www.codeproject.com/Articles/48575/How-to-Define-a-Template-Class-in-a-h-File-and-Imp    
void TempFunc()
{
    tsfresh::FisherTest<float> FisherTestFloatObj;
    tsfresh::FisherTest<double> FisherTestDoubleObj;
}

// main.cpp
#include <cassert>
#include <iostream>
#include <vector>
#include "fishers.h"
using namespace std;

bool floatEqual(double a, double b) {
    return fabs(a-b) <= 1e-6;
}

int main()
{
    auto fisher = tsfresh::FisherTest<double>();
    int a[] = {1,2,3,4};
    cout << fisher.pvalue(a[0], a[1], a[2], a[3]) << "\n";            
    cout << "All tests passed\n";
    return 0;
}

I need to hide implementation details in cpp file, so I need to separate header and source file of my template class. I make 2 instances of the template class that I want to use in the .cpp file (following the solution in the link above), but I still get linking error.

The compile command I use is: g++ -o test main.cpp fishers.cpp -std=c++17

I get the following error: main.cpp:(.text+0x93): undefined reference to tsfresh::FisherTest::pvalue(int, int, int, int)'`

What do I need to change to make this work? I always used header-only template classes, so I don't know what went wrong.

Upvotes: 0

Views: 164

Answers (1)

user12002570
user12002570

Reputation: 1

What do I need to change to make this work?

You need to put the implementation of class template FisherTest's member functions inside the header file fishers.h as shown below:

fishers.h

#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <math.h>
#include <cstring>

namespace tsfresh 
{

template<typename T=double>
class FisherTest {
static_assert(std::is_floating_point_v<T>, "FisherTest template T must be float or double");

public:        
    FisherTest();

    T pvalue(int a, int b, int c, int d);

private:    
    T pValueLog(int a, int b, int c, int d);
};
//implementation
template<typename T>
FisherTest<T>::FisherTest() {        
}

template<typename T>
T FisherTest<T>::pvalue(int a, int b, int c, int d) {
    return pValueLog(a,b,c,d);
}

template<typename T>
T FisherTest<T>::pValueLog(int a, int b, int c, int d) {
    return 0;
}
}

//declaration for tempfunc
void TempFunc();

fishers.cpp

#include "fishers.h"
void TempFunc()
{
    tsfresh::FisherTest<float> FisherTestFloatObj;
    tsfresh::FisherTest<double> FisherTestDoubleObj;
}

main.cpp

#include <cassert>
#include <iostream>
#include <vector>
#include "fishers.h"
#include <type_traits>
using namespace std;

bool floatEqual(double a, double b) {
    return fabs(a-b) <= 1e-6;
}

int main()
{
    auto fisher = tsfresh::FisherTest<double>();
    int a[] = {1,2,3,4};
    cout << fisher.pvalue(a[0], a[1], a[2], a[3]) << "\n";            
    cout << "All tests passed\n";
    return 0;
}

Demo.

Some of the modifications that i made include:

  1. Moved the implementation of member functions of class template FisherTest to the header fishers.h.
  2. Added a declaration for TempFunc inside header file fishers.h.
  3. The corresponding definition of TempFunc is indside the source file fishers.cpp.

Upvotes: 1

Related Questions