undefined reference to function error

Sep 5, 2019 at 2:13am
I made a data saving class called SaveStream that just saves variables to a chosen file and labels them so you can retrieve them. The problem is I haven’t even gotten past my store function. I just tested it with an integer and it gave me this error:

/tmp/main-abcc48.o: In function `main':
main.cpp:(.text+0xd6): undefined reference to `void SaveStream::store_as<int>(std::_cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int&)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
compiler exit status 1


main.cpp
1
2
3
4
5
6
7
8
9
#include <iostream>
#include "SaveStream.h"

int main(){
  std::cout << "Hello World!\n";
  int i = 17530;
  SaveStream saveVars("SAVS.txt");
  saveVars.store_as("i",i);
}


SaveStream.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include "SaveStream.h"

SaveStream::SaveStream(std::string saveTo){
  saveFile.open(saveTo); 
  if(!saveFile.is_open()){ // if the file doesn’t exist
    std::ofstream temp;
    temp.open(saveTo);     // make it as a new file
    saveFile.open(saveTo); // and then open the new file
  }
}

bool SaveStream::exists(std::string varName){
  bool does_exist = false;
  while(!saveFile.eof()){
    saveFile >> currVar;
    if(currVar == (varName + ":")){
      does_exist = true;
      break;
    }
  }
  return does_exist;
}

template <typename T>
void SaveStream::store_as(std::string varName,T& var){
  if(exists(varName)){
    int beginningPos = tellg();
    saveFile.seekp(beginningPos);
    char endingSymbol = ' ';
    while(endingSymbol == ';'){ // clears the line up to and including ending ';'
      saveFile.read(&endingSymbol,1);
      saveFile << ' ';
    }
    saveFile.seekp(beginningPos);
    saveFile << varName << ": " << var << " ;\n\n\n";
  }
  else {
    char dump;
    while(){
      // trying to find end so I can append stuff.
      saveFile.read(&dump,1);
    }
    saveFile.seekp( (saveFile.tellg() );
    saveFile << varName << ": " << var << " ;\n\n\n";
  }
}


SaveStream.h:
1
2
3
4
5
6
7
8
9
10
#include <fstream>

class SaveStream{
  std::fstream saveFile;
  public:
  SaveStream(std::string saveTo);
  bool exists(std::string varName);
  template <typeName T>
  void store_as(std::string varName,T& var);
};
Last edited on Sep 5, 2019 at 2:53pm
Sep 5, 2019 at 2:56am
Make sure you have your header inclusion guards in the header file.

SaveStream.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef SAVESTREAM_H
#define SAVESTREAM_H
#include <fstream>

class SaveStream{
  std::fstream saveFile;
  public:
  SaveStream(std::string saveTo);
  bool exists(std::string varName);
  /* should be typename, not typeName */
  template <typename T>
  void store_as(std::string varName,T& var);
};

#endif 


1
2
3
/* Remember that store_as belongs to the class SaveStream, so you have to say that it belongs to that class by using the scope operator */
template <typename T>
void SaveStream::store_as(std::string varName, T& var) { ... }
Sep 5, 2019 at 3:06am
A function template like store_as informs the compiler how to write store_as<foo> for you. Usually, all you need to do is attempt to use one of those functions, and the compiler will take your cue and write it for you at the point of use. This is called implicit instantiation.

For example, when you call the function template
1
2
  int foo = 24;
  my_savestream.store_as("foo", foo);

The compiler implicitly instantiates the function named store_as<int> for you, and emits a call to it.

In order for it to do this, it must see the entirety of the function template at the point it is required. This means that templates typically need to be defined in the header file containing their declaration.

Why can templates only be implemented in the header file?
https://stackoverflow.com/q/495021
Last edited on Sep 5, 2019 at 3:08am
Sep 5, 2019 at 2:57pm
@fiji885 I actually didn’t know about inclusion guards so thanks for that, also that was just a typo when I was rewriting the program on here (I had to put it in by hand which is actually usually super helpful to me too), thanks!

@mbozzi Oh, ok I’ll just transfer it then. Thank you!

:)
Sep 5, 2019 at 3:01pm
@fiji885 do I still include SaveStream.h in my .cpp file or is that not needed?

1
2
3
4
5
6
7
#include "SaveStream.h" // <- do I keep this
#ifndef SAVESTREAM_H
#define SAVESTREAM_H

// blah blah SaveStream.cpp code as normal

#endif 
Sep 5, 2019 at 4:28pm
It's good practice to have them. Why? Because if you're working in a program that requires the same header files and you have a lot of header files, those inclusion guards will prevent a recursive inclusion. If you do not have them, expect to receive an error saying something like void someClass::someFun() has already been declared in {someFile} .
Sep 5, 2019 at 4:57pm
Do I still include SaveStream.h in my .cpp file or is that not needed?

Yes, typically as the first meaningful line of code in the source file.

Implementation (.cpp) files are not typically included in other files. This means there is no potential for them to be included more than once, and so there is no need to protect against multiple inclusion by adding header guards to a source file.

Multiple inclusion typically occurs by including two distinct header files (a.hpp, b.hpp) that each include the same header file (e.g., <string>). In this hypothetical case, include guards are required in <string> to prevent its content from being included twice.

Furthermore, ensure that every header guard macro has a globally unique name.
Last edited on Sep 5, 2019 at 5:02pm
Sep 5, 2019 at 4:59pm
@highwayman

Those inclusion guards should go into SaveStream.h itself - not into the file that includes SaveStream.h.

Are you sure you've understood what inclusion guards are, and how they work? If not, I would strongly recommend you read up on them, to correct whatever misunderstanding you have.
Sep 5, 2019 at 6:57pm
Json, INI, YAML, don't use C++ streams. Streams are designed for pretty one one thing, which to read a bunch of data with only one type, and you don't care about newlines and spaces being transparent to eachother, you can still try but there are many benefits to using a 3rd party library when you have a wheel-like problem.
Sep 5, 2019 at 10:25pm
@fiji885, I think I’ve actually had quite a few of those errors before too.

@mbozzi thanks, thought so.

@Mikeyboy yeah.. that makes more sense ok I’ll move them.

@poteto why are we talking about Json, INI, and YAML now? I don’t get what your trying to say, what do you mean?
Sep 5, 2019 at 11:01pm
Can I include the .cpp file in the .h file instead of the other way around? The inclusion guards are making the the code not appear in the .cpp even though SAVESTREAM_H is not defined.

Edit: nvm.
Last edited on Sep 5, 2019 at 11:05pm
Topic archived. No new replies allowed.