1
votes

I am pretty new to c and c++, so please try explain more specific what I should do. The program tries to read files from a directory using multithreads, and store the information in a map so that it can be used later.

I have been looking for similar posts. However, I am not able to figure out.

In https://github.com/kaldi-asr/kaldi/issues/938, it said that "If you get linker errors about undefined references to symbols that involve types in the std::__cxx11 namespace or the tag [abi:cxx11] then it probably indicates that you are trying to link together object files that were compiled with different values for the _GLIBCXX_USE_CXX11_ABI macro."

The solution for undefined reference to `pthread_cancel' (add "-pthread" flag does not work either.

My code is

#include <iostream>
#include <iomanip>
#include <fstream>
#include <vector>
#include <map>
#include <algorithm>
#include <random>
#include <unistd.h>
#include <cmath>
#include <stdlib.h>
#include <mutex>
#include <sys/wait.h>
#include <filesystem>
#include <string>
#include <pthread.h>

#define time_str(s) (s < 60 ? (to_string(s) + " second(s)") : (s < 3600 ? (to_string((s) / 60) + " minute(s)") : (to_string((s) / 3600) + " hour(s) and " + to_string(((s) % 3600) / 60) + " minute(s)")))
using namespace std;
namespace fs = std::filesystem;

struct MyGenom
{
    vector<string> filepaths;
    map<string, string> seq;
};

void check_rv(int rv) {
    if (rv != 0) {
        printf("Error: Value is %d\n", rv);
        exit(1);
    }
}

struct Reference_Genome {
    static long unsigned int idx;
    static map <string, string> seq;
    static pthread_mutex_t mtxLock;
    static vector <string> filepaths;

    static void writing(string path) {
        ifstream rsf(path);
        string line, cur_chr;
        while (getline(rsf, line)) {
            if (line.substr(0, 1) == ">") {
                cur_chr = line.substr(1);
                seq[cur_chr] = "";
            } else
                seq[cur_chr] += line;
        }
        rsf.close();
    }

    static void *distribution(void *var) {
        string path;
        while (idx < filepaths.size()) {
            check_rv(pthread_mutex_lock(&mtxLock));
            path = filepaths[idx];
            idx++;
            check_rv(pthread_mutex_unlock(&mtxLock));
            writing(path);
        }
        return NULL;
    }

    Reference_Genome(string dir, unsigned int n_threads) {
        cout << "Reading the reference genome from \'" << dir << "\'" << endl;
        for (const auto &entry : fs::directory_iterator(dir))
            filepaths.push_back(entry.path());
        time_t t1 = time(NULL);
        idx = 0;
        // init lock
        int rv = pthread_mutex_init(&mtxLock, NULL);
        check_rv(rv);
        pthread_t workers[n_threads];
        int rs;
        struct MyGenom param;
        for (unsigned int th = 0; th < n_threads; th++) {
            rs = pthread_create(&workers[th], NULL, distribution, (void *)((unsigned long)th));
            check_rv(rs);
        }
        for (unsigned int th = 0; th < n_threads; th++) {
            rs = pthread_join(workers[th], NULL);
            check_rv(rs);
        }
        pthread_mutex_destroy(&mtxLock);

        time_t t2 = time(NULL);
        cout << filepaths.size() << " sequences were read in " << time_str((long) t2 - (long) t1)
             << ".\n----------------\n";
    }
};

int main(int argc, char const *argv[]) {
    string dir = "./data/ex_seq";
    unsigned int n_threads = 5;
    Reference_Genome ref(dir, n_threads);
    cout << "chr6: " << ref.seq["chr6"] << endl;
    cout << "chr9: " << ref.seq["chr9"] << endl;
    cout << "chr13: " << ref.seq["chr13"] << endl;
}

The gcc version is "Thread model: posix gcc version 9.3.0 (Ubuntu 9.3.0-10ubuntu2)".

The error is

testSeq.cpp:97: undefined reference to `Reference_Genome::seq[abi:cxx11]'
/usr/bin/ld: testSeq.cpp:98: undefined reference to `Reference_Genome::seq[abi:cxx11]'
/usr/bin/ld: testSeq.cpp:99: undefined reference to `Reference_Genome::seq[abi:cxx11]'
/usr/bin/ld: /tmp/cctfwVX2.o: in function `Reference_Genome::writing(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)':
/testSeq.cpp:46: undefined reference to `Reference_Genome::seq[abi:cxx11]'
/usr/bin/ld: testSeq.cpp:48: undefined reference to `Reference_Genome::seq[abi:cxx11]'
/usr/bin/ld: /tmp/cctfwVX2.o: in function `Reference_Genome::distribution(void*)':
testSeq.cpp:55: undefined reference to `Reference_Genome::filepaths[abi:cxx11]'
/usr/bin/ld: testSeq.cpp:55: undefined reference to `Reference_Genome::idx'
/usr/bin/ld: testSeq.cpp:56: undefined reference to `Reference_Genome::mtxLock'
/usr/bin/ld: testSeq.cpp:57: undefined reference to `Reference_Genome::idx'
/usr/bin/ld: testSeq.cpp:57: undefined reference to `Reference_Genome::filepaths[abi:cxx11]'
/usr/bin/ld: testSeq.cpp:58: undefined reference to `Reference_Genome::idx'
/usr/bin/ld: testSeq.cpp:58: undefined reference to `Reference_Genome::idx'
/usr/bin/ld: testSeq.cpp:59: undefined reference to `Reference_Genome::mtxLock'
/usr/bin/ld: /tmp/cctfwVX2.o: in function `Reference_Genome::Reference_Genome(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned int)':
testSeq.cpp:68: undefined reference to `Reference_Genome::filepaths[abi:cxx11]'
/usr/bin/ld: testSeq.cpp:70: undefined reference to `Reference_Genome::idx'
/usr/bin/ld: testSeq.cpp:72: undefined reference to `Reference_Genome::mtxLock'
/usr/bin/ld: testSeq.cpp:85: undefined reference to `Reference_Genome::mtxLock'
/usr/bin/ld: testSeq.cpp:88: undefined reference to `Reference_Genome::filepaths[abi:cxx11]'
collect2: error: ld returned 1 exit status

1

1 Answers

3
votes

When you declare static variables inside a class, you must also declare it exactly once outside of the class. In this case, you could put this in the bottom of your C++ file or in between the main() function and the class Reference_Genome definition:

long unsigned int Reference_Genome::idx;
map <string, string> Reference_Genome::seq;
pthread_mutex_t Reference_Genome::mtxLock;
vector <string> Reference_Genome::filepaths;

The idea is that you can put the class definition inside a header file, to be included in multiple different compilation units, but the static variables are only defined once, in one .cpp file of your choosing.