0

I am struggling to understand why my code won't compile. Currently my main.cpp file looks something akin to

using namespace std;

#include <iostream>
#include <vector>

#ifdef __cplusplus
extern "C"{
#endif

#include "sparse.h"

#ifdef __cplusplus
}
#endif

int main(){

        std::vector<long> tmp;

        tmp.push_back(29);
        tmp.push_back(50);
        tmp.push_back(9);

        sparse<double> a(tmp);

        cout << a.getDimensions() << endl;
        cout << a.storageEfficiency() << endl;

        cout << a.get(15) << endl;

        a.set(18, 2.5);

        cout << a.storageEfficiency() << endl;
}

and my sparse.h file looks like this:

using namespace std;

#include <iostream>
#include <numeric>
#include <vector>

template <class T>
class sparse
{
private:
        struct col {
                long index;
                T data;
                struct col* prev;
                struct col* next;
        };

        struct row {
                long index;
                struct col* cols;
                struct row* prev;
                struct row* next;
        };
        struct row* data;
        long dim;

public:
        sparse();
        sparse(vector<long> dimensions);
        T get(long i, long j);
        T get(long k);
        void set(long i, long j, T input);
        void set(long k, T input);
        long getDimensions();
        double storageEfficiency();
        ~sparse();
};

Similarly my sparse.cpp looks like this:

using namespace std;

#ifdef __cplusplus
extern "C"{
#endif

#include "sparse.h"

#ifdef __cplusplus
}
#endif

template <class T>
sparse<T>::sparse(){
        data = new struct row;
        dim = 0;
}

template <class T>
sparse<T>::sparse(vector<long> dimensions){
        data = new struct row;

        dim = accumulate(dimensions.begin(), dimensions.end(), 0);
}

template <class T>
T sparse<T>::get(long i, long j){

        for (struct row* tmp_row = data; tmp_row != NULL; tmp_row = tmp_row->next){
                if (tmp_row->index > i)
                        return 0;
                else if (tmp_row->index == i){
                        for (struct col* tmp_col = tmp_row->cols; tmp_col != NULL; tmp_col = tmp_col->next){
                                if (tmp_col->index > j)
                                        return 0;
                                else if (tmp_col->index == j)
                                        return tmp_col->data;
                        }
                }

        }
        return NULL;
}

template <class T>
T sparse<T>::get(long k){
        return get(k%getDimensions(), k/getDimensions());
}

template <class T>
void sparse<T>::set(long i, long j, T input){

        if (i >= dim || j >= dim){
                // TODO: Throw C++ equivalent to ArrayOutOfBounds Exception
        }

        for (struct row* tmp_row = data; tmp_row != NULL; tmp_row = tmp_row->next){
                if (tmp_row->index > i){
                        struct row* rtmp = new struct row;
                        rtmp->index = i;
                        rtmp->next = tmp_row;
                        tmp_row = tmp_row->prev;
                        rtmp->next->prev = rtmp;
                        rtmp->prev = tmp_row;
                        tmp_row->next = rtmp;

                        struct col* ctmp = new struct col;
                        rtmp->cols = ctmp;
                        ctmp->index = j;
                        ctmp->data = input;
                        return;
                } else if (tmp_row->index == i){
                        for (struct col* tmp_col = tmp_row->cols; tmp_col != NULL; tmp_col = tmp_col->next){
                                if (tmp_col->index > j){
                                        struct col* ctmp = new struct col;
                                        ctmp->next = tmp_col;
                                        tmp_col = tmp_col->prev;
                                        ctmp->next->prev = ctmp;
                                        ctmp->prev = tmp_col;
                                        tmp_col->next = ctmp;

                                        ctmp->index = j;
                                        ctmp->data = input;
                                        return;
                                }
                                else if (tmp_col->index == j){
                                        tmp_col->data = input;
                                        return;
                                }
                        }
                }
        }
}

template <class T>
void sparse<T>::set(long k, T input){
        set(k%getDimensions(), k/getDimensions(), input);
}

template <class T>
long sparse<T>::getDimensions(){
        return dim;
}

template <class T>
double sparse<T>::storageEfficiency(){
        long numCells = 0;
        for (struct row* rtmp = data; rtmp != NULL; rtmp = rtmp->next){
                for (struct col* ctmp = rtmp->cols; ctmp != NULL; ctmp = ctmp->next){
                        numCells++;
                }
        }
        return (double)numCells/dim;
}

My cmake file looks as such:

cmake_minimum_required(VERSION 3.0.0)
project(SPARSE LANGUAGES CXX)

add_executable(
    sparse

    main.cpp
    sparse.cpp
)

Somehow whenever I compile the project using cmake, and gcc/g++ for that matter, I am faced with errors such as this: Template with C linkage or undefined reference to 'sparse<double>::getDimensions()' How do I fix this, and why is it happening?

Ohunter
  • 343
  • 1
  • 2
  • 13
  • 1
    Whats the `extern "C"` for? The header file doesn't look like a C file... – tkausl Jun 27 '19 at 11:54
  • It was an attempt to fix it. Without it I get the undefined reference, and with it I get the template with C linkage – Ohunter Jun 27 '19 at 11:56
  • Given the `extern "C"` business was an attempt to fix the underlying problem, I'll close this as a dupe addressing the latter. – Baum mit Augen Jun 27 '19 at 11:58
  • you need the sparse.h in the cmake as wall – alon Jun 27 '19 at 12:01
  • Where would I include the header in the cmake file? – Ohunter Jun 27 '19 at 12:02
  • I usually do this: file (GLOB CPP_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file (GLOB H_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*.h") set (SOURCE_FILES ${CPP_FILES} ${H_FILES}) add_executable(sparse ${SOURCE_FILES}) – alon Jun 27 '19 at 12:04
  • doing that only resulted in undefined reference – Ohunter Jun 27 '19 at 12:14
  • @alon: "you need the sparse.h in the cmake as wall" - Not quite true. Listing a header in the `add_executable` call may be used when given header is create by CMake or when one want the header files listed in IDE. Otherwise, including the header into `add_executable` call has no effect. – Tsyvarev Jun 27 '19 at 12:19
  • @Tsyvarev then how do I fix it? – Ohunter Jun 27 '19 at 13:51
  • @Ohunter: Have you read the question post, marked as duplicate of yours one? Methods of a template class should be implemented in the header file itself, not in the source file. – Tsyvarev Jun 27 '19 at 14:04

0 Answers0