This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler/66AK2G12: How to resolve an undefined symbol linker error in a multi file project?

Part Number: 66AK2G12


Tool/software: TI C/C++ Compiler

Hello I am encountering a problem with the compilation of some basic c++ code in a multi file project. 

I am using object oriented techniques in c++. This project has three files in totality:

VecN.hpp and VecN.cpp define the a class and the implementation of it's member functions.

main.cpp is the main file that instantiates an object and does some operations using it's member functions.

A full copy of the build log is attached.

Can you spot what am I doing wrong in my code ? I suspect it's something related to the linking order and linker settings because I have managed to compile similar code using the g++ v10.2.0 in cygwin and a simple Makefile.  

**** Build of configuration Debug for project testing-VecN-library ****

"C:\\ti\\ccs1000\\ccs\\utils\\bin\\gmake" -k -j 4 all -O 
 
Building file: "../VecN.cpp"
Invoking: C6000 Compiler
"C:/ti/ccs1000/ccs/tools/compiler/ti-cgt-c6000_8.3.7/bin/cl6x" -mv6600 --include_path="C:/..." --include_path="C:/ti/ccs1000/ccs/tools/compiler/ti-cgt-c6000_8.3.7/include" -g --c99 --c++14 --diag_warning=225 --diag_wrap=off --display_error_number  "../VecN.cpp"
Finished building: "../VecN.cpp"
 
Building file: "../main.cpp"
Invoking: C6000 Compiler
"C:/ti/ccs1000/ccs/tools/compiler/ti-cgt-c6000_8.3.7/bin/cl6x" -mv6600 --include_path="C:/..." --include_path="C:/ti/ccs1000/ccs/tools/compiler/ti-cgt-c6000_8.3.7/include" -g --c99 --c++14 --diag_warning=225 --diag_wrap=off --display_error_number  "../main.cpp"
"../main.cpp", line 14: warning #552-D: variable "tmp" was set but never used
Finished building: "../main.cpp"
 
Building target: "testing-VecN-library.out"
Invoking: C6000 Linker
"C:/ti/ccs1000/ccs/tools/compiler/ti-cgt-c6000_8.3.7/bin/cl6x" -mv6600 -g --c99 --c++14 --diag_warning=225 --diag_wrap=off --display_error_number -z -m"testing-VecN-library.map" --heap_size=0x4000 --stack_size=0x1000 -i"C:/ti/ccs1000/ccs/tools/compiler/ti-cgt-c6000_8.3.7/lib" -i"C:/ti/ccs1000/ccs/tools/compiler/ti-cgt-c6000_8.3.7/include" --reread_libs --define=DSP_CORE=1 --diag_wrap=off --display_error_number --warn_sections --xml_link_info="testing-VecN-library_linkInfo.xml" --rom_model -o "testing-VecN-library.out" "./VecN.obj" "./main.obj" "../66AK2Gxx_C66.cmd"  -llibc.a 
<Linking>
 
 undefined                              first referenced
  symbol                                    in file     
 ---------                              ----------------
 VecN<float, (unsigned int)3>::iszero() ./main.obj      
 
error #10234-D: unresolved symbols remain
error #10010: errors encountered during linking; "testing-VecN-library.out" not built
 
>> Compilation failure
makefile:142: recipe for target 'testing-VecN-library.out' failed
gmake[1]: *** [testing-VecN-library.out] Error 1
makefile:138: recipe for target 'all' failed
gmake: *** [all] Error 2

**** Build Finished ****

  • It is likely this problem first appeared in a larger project, and you cut it down into a smaller project which more clearly demonstrates the problem.  Thank you!  Please submit this smaller project.  Create a zip file of the project by following the directions in the article Sharing projects.  Then attach that zip file to your next post.

    Thanks and regards,

    -George

  • Hello George.

    Thank you for your reply

    I have stripped down the project to it's simplest form possible and was still able to re-create the problem in a new project. This was then exported and zipped following the guidlines you provided.

    As an extra step I imported the resulting project in an ubuntu virutal machine but encountered the very same error. 

    8713.test-project.zip

  • When the declaration of the template functions zero and iszero were moved from VecN.cpp to VecN.hpp then the linker errors no longer occur.

    Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file? explains the reason for trying this change.

    Not sure if there is a better way of solving the problem.

  • You've correctly identified the issue and its resolution.

    Templates are not definitions of an object, but rather are blueprints from which the compiler can construct the definition of an object.

    template<class _Tp, unsigned N>
    void VecN<_Tp, N>::zero()
    {
        for(unsigned i=0; i<N; i++) _a[i]= 0;
    }

    This code does not define the VecN<float, 3>::zero function. Instead it provides the blueprint to define that function should an instantiation of that template be encountered. Since this instantiation is not encountered in VecN.cpp, no function is defined. Therefore, the program fails to link.

    Unlike non-template member functions, you should not place the definitions of template class member functions in another source file. They should be in the .hpp file.

    There is a feature that will allow you to do this, but I'm not sure it'd help much, and can be especially messy in a larger project where VecN is instantiated in many different ways: The concept of 'extern template'.


    https://en.cppreference.com/w/cpp/language/class_template

    In the .hpp, you can define the template class, and then write the statement 'extern template VecN<float>'. This tells the compiler that there is a definition of VecN<float, 3> available to the program, but it's going to be defined explicitly elsewhere.

    Then, in the VecN.cpp file, define template class member functions for ::zero and ::iszero, then write the statement 'template VecN<float>;'. This statement, called an explicit instantiation, instructs the compiler to define the class VecN<float, 3> and all of its member functions in VecN.cpp. Since the definitions of ::zero and ::iszero are available, they are defined as well.

    Still, I suggest that you avoid defining template class member functions outside of their header.

  • Thank you for your responses. I am quite new to using templates and C++ in general. My take from this is that :

    1. A template is a definition of a class or function. The compiler will only generate code when an instance of the declaraced class/function is encountered. 
    2. To generate code when the compiler encounters an instance, it must have the declaration available - hence why it the implementations of the 'member functions' should be present in the .hpp file.

    Thank you for your help

  • Your understanding of this situation is correct.  That said, I want to caution you on your use of certain terms.

    In particular, the terms declaration and definition mean very particular things.  Please read about them in this C FAQ.  Even though that is a C FAQ, the meaning of those terms in C++ is the same.  

    Thanks and regards,

    -George