0

I have a very simple project with two sources and one header:

  • math_precision_io_test.cc (containing main). main contains the following lines:

    int retval = 0;
    retval = write_datum<float>(file1, &f2, "float");
    retval = write_datum<double>(file2, &d2, "double");
    
  • math_precision_io.cc, containing the functions to be used, including

    template<class T>
    int write_datum(std::ofstream & file, const T * datum, const char* descr) {
        static_assert(std::is_same<T, double>::value ||
                std::is_same<T, float>::value ||
                std::is_same<T, int>::value ||
                std::is_same<T, char>::value,
                "write_datum is not defined on this class");
    
        if (file.good()) {
            const size_t data_size = sizeof(T);
            file.write((char *) datum, data_size);
            if (descr != NULL) {
                std::cout << "Writing datum " << descr << std::endl;
            }
            return 0;
        } else {
            return 1;
        }
    }
    
  • math_precision_io.h, included in math_precision_io_test.cc, with (among others) the declaration

    template<class T>
    int write_datum(std::ofstream & file, const T * datum, const char* descr = NULL);
    

Both sources compile ok (objects are placed in dir obj/). When linking with

g++ -g -g3 -Wall -Wunused -Wuninitialized -Wextra -fmessage-length=0 -std=gnu++11 -o math_precision_io_test obj/math_precision_io_test.o obj/math_precision_io.o 

I get

math_precision_io_test.cc:74: undefined reference to `int write_datum<float>(std::basic_ofstream<char, std::char_traits<char> >&, float const*, char const*)'
math_precision_io_test.cc:75: undefined reference to `int write_datum<double>(std::basic_ofstream<char, std::char_traits<char> >&, double const*, char const*)'

But if I only move the lines of function write_datum from math_precision_io.cc to math_precision_io_test.cc, compile and link, the error disappears.

I cannot understand why.

What is the reason for the error, and how can it be fixed? (meaning keeping the function definition in math_precision_io.cc and linking ok).

Note: The solution to the problem I found is indeed present in answers to the question linked to at the top. But the question itself is different, and one may not find the connection between the two right away. That is precisely what happened to me (otherwise, I would not ask the question).

  • A template implementation has to be visible from the header, please move the implementation (preferably) to the header as an inline definition. Either that, or you have to explicitly say which instantiation(s) of the template will exist (if you know which ones will be used: https://en.cppreference.com/w/cpp/language/class_template#Explicit_instantiation ). – N00byEdge Sep 10 '18 at 08:58
  • 1
    @N00byEdge If you look at the first line of `write_datum` it's pretty clear that he does know which types will be used. So explicit instantiation seems the correct solution. – john Sep 10 '18 at 09:23
  • @john Good eye! Yep, explicit instantiation does sound ideal here. – N00byEdge Sep 10 '18 at 09:25
  • @john - But the I guess I will have to replicate the explicit instantiation for each clas, and whenever I modify the function I will have to replicate that too. The objective of templating is avoiding that, only leaving to the static_assert the selection of allowed classes. – sancho.s ReinstateMonicaCellio Sep 10 '18 at 09:50
  • @sancho.s If you don't want to use explicit instantiation then the only other possibility is to put the implementation in the header file (or redesign your code). – john Sep 10 '18 at 10:05
  • @john - But does explicit instantiation demand that all code be replicated as many times as target classes needed? (I guess the obvious answer is Yes, but just in case...) – sancho.s ReinstateMonicaCellio Sep 10 '18 at 10:08
  • @sancho.s No, you still only have one template but you explicitly instantiate it for each type, like this `template int write_datum(std::ofstream &, const float *, const char*); template int write_datum(std::ofstream &, const int *, const char*); ...` Not much more code than a function prototype. – john Sep 10 '18 at 10:16
  • 1
    @john - Excellent. I would add: 1) The explicit instantiations should be located in the source file along with the template (not in the header). 2) If the template function changes, these changes should be written only once (unless the prototype itself changes, in which case all the instantiations should be changed). – sancho.s ReinstateMonicaCellio Sep 10 '18 at 11:14

0 Answers0