Search code examples
c++headerheader-files

Compiling code using personal C++ library breaks when used in conjunction with header files


I have some C++ utility functions I've been using across projects. I want to make a library out of those utilities to service projects without having to copy/paste any changes I might make.

I can turn the single .cpp file into a library with:

$ g++ -c util.cpp
$ ar rcs libutil.a util.o

and made a util.h header with all of the functions.

This library works to compile and run a simple test.cpp, which prints a dot and the mean of a vector using the library functions: (I moved the header to ~/.local/include/ and the library to ~/.local/lib/)

$ g++ -o test test.cpp -L ~/.local/lib/ -lutil
$ ./test
.
4.5

However, when I try to compile (parts of) a project with the library, I get "{function} was not declared in this scope" errors.

$ g++ -c source/linreg.cpp -L ~/.local/lib/ -lutil
...
linreg.cpp:11:18: error: ‘vecMean’ was not declared in this scope
...

Trying to reproduce this behavior I wrote this:

// header.h
#ifndef HEADER_H
#define HEADER_H

void test();

#endif

// main.cpp
#include "header.h"
#include "util.h"

int main()
{
    dot();
    test();
    return 0;
}

// test.cpp
#include <string>
#include <vector>
#include <iostream>
#include "util.h"
#include "header.h"

void test()
{
  dot();
  std::vector<double> x;
  for(int i = 0; i < 10; ++i)
      x.push_back(i * 1.0);
  std::cout << vecMean(x) << std::endl;
}

Which does not compile. Depending on which of the #includes precedes the other, different errors are thrown.

The above throws "'dot' was not declared in this scope", while the below throws "'test' was not declared in this scope"

// main.cpp
#include "util.h"
#include "header.h"
...

This is the same kind of behavior I see when I try to compile my actual project.

If I remove the dot() call from main.cpp the example compiles and runs fine, except when placing the util.h include statement before the header.h one (although I guess the util.h include is pointless). This leads to 'test' not being declared.

I feel like I'm missing something obvious, even though the entire process of learning to set up a library has been a struggle.

Seeing as header files appear to be part of the problem I'm adding my util.h below, as well as util.cpp, for good measure.

#ifndef HEADER_H
#define HEADER_H

#include <vector>
#include <tuple>
#include <fstream>
#include <string>

/***** utils *****/
// smallest/largest value from a vector
int indexSmallest(const std::vector<double> &vec);
int indexLargest(const std::vector<double> &vec);
// some vector operations
std::vector<double> sclMult(const std::vector<double> &vec, double scl);
std::vector<double> sclAdd(const std::vector<double> &vec, double scl);
std::vector<double> vecAdd(const std::vector<double> &vec1, const std::vector<double> &vec2);
std::vector<double> vecSub(const std::vector<double> &vec1, const std::vector<double> &vec2);
std::vector<std::vector<double> > vecCat(const std::vector<double> &vec1,
                                         const std::vector<double> &vec2,
                                         const std::vector<double> &vec3);
double vecMean(const std::vector<double> &vec);
double vecSum(const std::vector<double> &vec);
// sort two vectors of length 3 by the elements in the err vector
std::tuple<std::vector<std::vector<double> >, std::vector<double> >
  sort(const std::vector<std::vector<double> > &X, const std::vector<double> &err);
// return maximum and minimum values from vector
std::vector<double> topbot(std::vector<double> &vec);
// print a dot
void dot(std::string str = ".");
// print a vector of doubles
void printVec(std::vector<double> vec);
// print a matrix of doubles
void printMat(std::vector<std::vector<double> > mat);

#endif
#include <vector>
#include <tuple>
#include <cmath>
#include <iostream>
#include <string>

#include "util.h"

int indexSmallest(const std::vector<double> &vec)
{
  int index = 0;
  for(int i = 1; i < vec.size(); i++)
    {
      if(vec[i] < vec[index])
        index = i;
    }
  return index;
}

int indexLargest(const std::vector<double> &vec)
{
  int index = 0;
  for(int i = 1; i < vec.size(); i++)
    {
      if(vec[i] > vec[index])
        index = i;
    }
  return index;
}

std::vector<double> sclMult(const std::vector<double> &vec, double scl)
{
  std::vector<double> vvec(vec.size());
  for(int i = 0; i < vec.size(); i++){
    vvec[i] = vec[i] * scl;
  }
  //printVec(vvec);
  return vvec;
}
std::vector<double> sclAdd(const std::vector<double> &vec, double scl)
{
  std::vector<double> vvec(vec.size());
  for(int i = 0; i < vec.size(); i++)
    vvec[i] = vec[i] + scl;
  return vvec;
}
std::vector<double> vecAdd(const std::vector<double> &vec1, const std::vector<double> &vec2)
{
  std::vector<double> vvec(vec1.size());
  //std::cout << "aaaa ";
  //printVec(vec1);
  for(int i = 0; i < vec1.size(); i++){
    vvec[i] = (vec1[i] + vec2[i]);
  }
  return vvec;
}
std::vector<double> vecSub(const std::vector<double> &vec1, const std::vector<double> &vec2)
{
  std::vector<double> vvec(vec1.size());
  for(int i = 0; i < vec1.size(); i++)
    vvec[i] = (vec1[i] - vec2[i]);
  //vvec.push_back(vec1[i] - vec2[i]);
  return vvec;
}
std::vector<std::vector<double> > vecCat(const std::vector<double> &vec1,
                                         const std::vector<double> &vec2,
                                         const std::vector<double> &vec3)
{
  std::vector<std::vector<double> > vecCat(3);
  vecCat[0] = vec1;
  vecCat[1] = vec2;
  vecCat[2] = vec3;
  return vecCat;
}
std::tuple<std::vector<std::vector<double> >, std::vector<double> >
sort(const std::vector<std::vector<double> > &X, const std::vector<double> &err)
{
  //std::cout << X.size() << ' ' << err.size() << std::endl;
  std::vector<double> sortErr(3);
  //std::vector<std::vector<double> > sortX;
  int small = indexSmallest(err), large = indexLargest(err);
  if(small == large)
    return std::make_tuple(X,err);
  int middle = fabs(small + large - 3);
  //std::cout << small << ' ' << middle <<  ' ' << large << std::endl;
  sortErr[0] = err[small];
  sortErr[1] = err[middle];
  sortErr[2] = err[large];
  std::vector<std::vector<double> > sortX = vecCat(X[small],X[middle],X[large]);
    /*  sortX[0] = X[small];
  sortX[1] = X[middle];
  sortX[2] = X[large];*/
  return std::make_tuple(sortX,sortErr);
}

double vecMean(const std::vector<double> &vec)
{
  double sum = 0;
  for(int i = 0;i < vec.size();i++){
    sum += vec[i];
  }
  return sum / vec.size();
}
double vecSum(const std::vector<double> &vec)
{
  double sum = 0;
  for(int i = 0;i < vec.size();i++){
    sum += vec[i];
  }
  return sum;
}

void dot(std::string str)
{
  std::cout << str << std::endl;
}

std::vector<double> topbot(std::vector<double> &vec)
{
  double top = vec[0];
  double bot = vec[0];
  for(int i = 1; i < vec.size(); ++i){
    if(vec[i] > top)
      top = vec[i];
    if(vec[i] < bot)
      bot = vec[i];
  }
  std::vector<double> topbot = {top,bot};
  return topbot;
}

void printVec(std::vector<double> vec)
{
  for(int i = 0; i < vec.size(); ++i){
    std::cout << vec[i] << ',';
  }
  std::cout << std::endl;
}
void printMat(std::vector<std::vector<double> > mat)
{
  for(int i = 0; i < mat.size(); ++i){
    printVec(mat[i]);
  }

}
std::vector<double> head(std::vector<double> vec, int n)
{
  std::vector<double> head;
  for(int i = 0; i < n; ++i)
    head.push_back(vec[i]);
  return head;
}
std::vector<double> tail(std::vector<double> vec, int n)
{
  std::vector<double> tail;
  for(int i = vec.size() - n; i < vec.size(); ++i)
    tail.push_back(vec[i]);
  return tail;
}
std::vector<double> normalize(std::vector<double> vec)
{
  std::vector<double> tb = topbot(vec);
  std::vector<double> norm;
  for(int i = 0; i < vec.size(); ++i)
    norm.push_back((vec[i] - tb[1]) / (tb[0] - tb[1]));
  return norm;
}
std::vector<double> vecLog(std::vector<double> vec)
{
  std::vector<double> logged;
  for(int i = 0; i < vec.size(); ++i)
    logged.push_back(std::log(vec[i]));
  return logged;
}
std::vector<double> vecExp(std::vector<double> vec)
{
  std::vector<double> logged;
  for(int i = 0; i < vec.size(); ++i)
    logged.push_back(std::exp(vec[i]));
  return logged;
}

Solution

  • The problem is that you have the SAME include guard in both headers:

    #ifndef HEADER_H
    #define HEADER_H
    

    So then one of the two files -- here, util.h -- is being skipped because that symbol has in fact already been defined.

    May I also recommend from experience that you name the library something more unique -- say, verkutil -- with an include guard to match? My experience is that too many projects have their own UTIL_H symbol and similarly named files.