c++linkerc++17

Duplicates found when linking C++ libraries


I've made a few new classes for help in reading CSV files in a C++ program, based on code suggested in another stackoverflow post here. Here is the file declaring and defining the classes, along with some helper functions defined outside the classes to convert to different data types:

/**
@file CSVHandler.h
@brief Declaration of a class for handling CSV file reading and writing.
*/

#ifndef SRC_THAMESLIB_CSVHANDLER_H_
#define SRC_THAMESLIB_CSVHANDLER_H_

#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>

/**
@class Declare the CSVRow class

This class was taken from an answer at stackoverflow.com:
https://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv-files-in-c
*/
class CSVRow {
public:
  std::string_view operator[](std::size_t index) const {
    return std::string_view(&m_line[m_data[index] + 1],
                            m_data[index + 1] - (m_data[index] + 1));
  }
  std::size_t size() const { return m_data.size() - 1; }
  void readNextRow(std::istream &str) {
    std::getline(str, m_line);

    m_data.clear();
    m_data.emplace_back(-1);
    std::string::size_type pos = 0;
    while ((pos = m_line.find(',', pos)) != std::string::npos) {
      m_data.emplace_back(pos);
      ++pos;
    }
    // This checks for a trailing comma with no data after it.
    pos = m_line.size();
    m_data.emplace_back(pos);
  }

private:
  std::string m_line;
  std::vector<int> m_data;
};

std::istream &operator>>(std::istream &str, CSVRow &data) {
  data.readNextRow(str);
  return str;
}

/**
@class Declare the CSVIterator class

This class was taken from an answer at stackoverflow.com:
https://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv-files-in-c
*/
class CSVIterator {
public:
  typedef std::input_iterator_tag iterator_category;
  typedef CSVRow value_type;
  typedef std::size_t difference_type;
  typedef CSVRow *pointer;
  typedef CSVRow &reference;

  CSVIterator(std::istream &str) : m_str(str.good() ? &str : nullptr) {
    ++(*this);
  }
  CSVIterator() : m_str(nullptr) {}

  // Pre Increment
  CSVIterator &operator++() {
    if (m_str) {
      if (!((*m_str) >> m_row)) {
        m_str = nullptr;
      }
    }
    return *this;
  }
  // Post increment
  CSVIterator operator++(int) {
    CSVIterator tmp(*this);
    ++(*this);
    return tmp;
  }
  CSVRow const &operator*() const { return m_row; }
  CSVRow const *operator->() const { return &m_row; }

  bool operator==(CSVIterator const &rhs) {
    return ((this == &rhs) ||
            ((this->m_str == nullptr) && (rhs.m_str == nullptr)));
  }
  bool operator!=(CSVIterator const &rhs) { return !((*this) == rhs); }

private:
  std::istream *m_str;
  CSVRow m_row;
};

/**
@class Declare the CSVRange class

This class was taken from an answer at stackoverflow.com:
https://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv-files-in-c
*/
class CSVRange {
  std::istream &stream;

public:
  CSVRange(std::istream &str) : stream(str) {}
  CSVIterator begin() const { return CSVIterator{stream}; }
  CSVIterator end() const { return CSVIterator{}; }
};

// Handler functions to convert to different data types
int row2int(const std::string_view &string) {
  int result = std::stoi(std::string(string));
  return (result);
}

double row2double(const std::string_view &string) {
  double result = std::stod(std::string(string));
  return (result);
}

#endif // SRC_THAMESLIB_CSVHANDLER_H_

The problem is that when I now compile the library I get a linking error saying that these helper functions and the overloaded >> operator are duplicates:

[ 73%] Built target thameslib
[ 78%] Linking CXX executable thames
duplicate symbol 'row2int(std::__1::basic_string_view<char, std::__1::char_traits<char>> const&)' in:
    /Users/jwbullard/Software/THAMES/build/CMakeFiles/thames.dir/src/thames.cc.o
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[11](Lattice.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[9](KineticController.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[4](Controller.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[15](StandardKineticModel.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[13](PozzolanicModel.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[12](ParrotKillohModel.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[10](KineticModel.cc.o)
duplicate symbol 'row2double(std::__1::basic_string_view<char, std::__1::char_traits<char>> const&)' in:
    /Users/jwbullard/Software/THAMES/build/CMakeFiles/thames.dir/src/thames.cc.o
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[11](Lattice.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[9](KineticController.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[4](Controller.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[15](StandardKineticModel.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[13](PozzolanicModel.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[12](ParrotKillohModel.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[10](KineticModel.cc.o)
duplicate symbol 'operator>>(std::__1::basic_istream<char, std::__1::char_traits<char>>&, CSVRow&)' in:
    /Users/jwbullard/Software/THAMES/build/CMakeFiles/thames.dir/src/thames.cc.o
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[11](Lattice.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[9](KineticController.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[4](Controller.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[15](StandardKineticModel.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[13](PozzolanicModel.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[12](ParrotKillohModel.cc.o)
    /Users/jwbullard/Software/THAMES/build/src/thameslib/libthameslib.a[10](KineticModel.cc.o)
ld: 3 duplicate symbols
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [thames] Error 1

I thought that having the header guards would prevent this kind of duplication, but I guess not. Can anyone give me some advice on how to correct this?


Solution

  • I thought that having the header guards would prevent this kind of duplication, but I guess not.

    The header guard prevents duplicate symbols only when your header file is included multiple times within a single translation unit (TU), ie a .cc file in your case.

    But, when the header file is included in multiple TUs, each TU defines the same symbols within itself. TUs are compiled independently of each other. The linker then ends up finding the duplicates when it brings together the .o object files for those multiple TUs. That is why you get a linker error and not a compiler error.

    To fix this, you can either:

    or: