1

I created a library in C++ called libparse, the code is used by another library named libitcmpmsg.so.

I've been trying to test libitcmpmsg in test.cpp, however when I try to build it the compiler returns the following messages:

$libpath/libitcmpmsg.so: reference not found to "void MSG_PARSER::WriteBigEndian<unsigned char>(std::vector<unsigned char, std::allocator<unsigned char> &, unsingned char)"
$libpath/libitcmpmsg.so: reference not found to "unsigned char* MSG_PARSER::ReadBigEndian<unsigned char>(unsigned char&, unsigned char*, unsigned int&)"
$libpath/libitcmpmsg.so: reference not found to "MSG_PARSER::ReadFixedLengthString(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, unsigned char*, int, unsigned int&)"
$libpath/libitcmpmsg.so: reference not found to "MSG_PARSER::WriteFixedLengthString(std::vector<unsigned char, std::allocator<unsigned char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int)"

libpath just represents the path to the library. MSG_PARSER is the namespace from libparser.

The functions' scopes, from the error messages, in msg_parser.h are:

    template <class T>
    void WriteBigEndian(std::vector<uint8_t>& target, T source);

    template <class T>
    uint8_t* ReadBigEndian(T &target, uint8_t* source, uint32_t &available);

    uint8_t* ReadFixedLengthString(string& target, uint8_t *source, int size, uint32_t &available);

    void WriteFixedLengthString(std::vector<uint8_t> &target, const string& source, uint32_t size);

It seems that libitcmpmsg is passing the wrong type of arguments to libparser.so. Below, it is a code snippet where libparer.so is being used by libitcmpmsg.so

#include "ptcinteraction.h"
#include "msg_parser.h"
#include <stdint.h>

using namespace PTC_INTERACTION;
using namespace MSG_PARSER;


PtcInteraction::PtcInteraction( std::vector<uint8_t> &data )
{
    m_msgBuffer.clear();
    uint32_t tavailable = static_cast<uint32_t>(data.size());
    uint8_t *tsource = &data[0];
    uint8_t value = 0;

    tsource = ReadFixedLengthString(m_message.railScac, tsource,  SCAC_SIZE, tavailable);
    tsource = ReadBigEndian<uint8_t>(m_message.sizeOfPromptTxt, tsource, tavailable );
    tsource = ReadFixedLengthString(m_message.promptTxt, tsource,  m_message.sizeOfPromptTxt, tavailable);
    tsource = ReadBigEndian<uint8_t>(m_message.sizeOfKeyPressedTxt, tsource, tavailable );
    tsource = ReadFixedLengthString(m_message.keyPressedTxt, tsource,  m_message.sizeOfKeyPressedTxt, tavailable);

    if((&data[0] + data.size()) == tsource)
    {
        m_msgBuffer = data;
    }
    else
    {
        m_msgBuffer = {};
    }
}

PtcInteraction::PtcInteraction( OCCPTCMSG::PtcInteractionT &ptcInteraction)
{
    m_msgBuffer.clear();
    m_message = ptcInteraction;

    WriteFixedLengthString(m_msgBuffer, ptcInteraction.railScac, SCAC_SIZE);
    WriteBigEndian<uint8_t>(m_msgBuffer, ptcInteraction.sizeOfPromptTxt );
    .
    .
    .

PTCInteraction is a class from libitcmpmsg.so, while PtcInteractionT is a strucuture also defined by libitcmpmsg.

The test code is represented below:

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include "ptcinteraction.h"
#include "occptcmessages_generated.h"
#include "msg_parser.h"
#include <cstdio>

using namespace PTC_INTERACTION;

int main(void)
{
    OCCPTCMSG::PtcInteractionT teste;


    teste.railScac = "abcd";
    teste.promptTxt = "message test";
    teste.sizeOfPromptTxt = teste.promptTxt.size();
    teste.keyPressedTxt = "test";
    teste.sizeOfKeyPressedTxt = teste.keyPressedTxt.size();

    PTC_INTERACTION::PtcInteraction ptcInter(teste);

    PTC_INTERACTION::PtcInteraction ptcInter2(ptcInter.m_msgBuffer);

    if ( (ptcInter.m_message.railScac == ptcInter2.m_message.railScac) &&
       (ptcInter.m_message.promptTxt == ptcInter2.m_message.promptTxt) &&
       (ptcInter.m_message.sizeOfPromptTxt == ptcInter2.m_message.sizeOfPromptTxt) &&
       (ptcInter.m_message.keyPressedTxt == ptcInter2.m_message.keyPressedTxt) &&
       (ptcInter.m_message.sizeOfKeyPressedTxt == ptcInter2.m_message.sizeOfKeyPressedTxt) )
    {
        std::cout << "Serialization and deserialization succeeded" << std::endl;
    }

}

There are 3 CMakeLists employed in the code development:

  • libparser builder;
  • libitcmpmsg builder;
  • test builder.

Does anyone know why compiler returns the 4 error messages described in the beginning of the question?

How can I solve the problems?

Let me know if you need CMakeLists code to better understand the problem.

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
André Machoski
  • 47
  • 1
  • 1
  • 7
  • Are your template functions defined (the body of the funcion) in a header (h) or a body (cpp) file? – Manuel Jul 03 '20 at 17:57
  • They are defined in cpp files. – André Machoski Jul 03 '20 at 18:20
  • See [this question](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) for the template functions. Where are `ReadFixedLengthString` and `WriteFixedLengthString` defined? [This](https://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix) may help. – 1201ProgramAlarm Jul 03 '20 at 18:53
  • ReadFixedLengthString and WriteFixedLengthString were defined in msg_parser.cpp. I relocated the location of the template function to the header file and the erros regarding them has gone. The functions that don't use template I also move them to the header and the code compiled. Why did I have to move the non-template functions too? – André Machoski Jul 03 '20 at 20:49
  • "Why did I have to move the non-template functions too?" - You haven't to define non-template functions in the header file. Probably, something else is wrong with your definition of functions `ReadFixedLengthString` and `WriteFixedLengthString`. But since you don't show this **definition** (not a *declaration*), we could only guess about the reasoning. – Tsyvarev Jul 04 '20 at 00:06

1 Answers1

2

You have to instantiate the specialized template classes in the cpp, or either put the body of template classes in the header: https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl

Frazzo
  • 371
  • 2
  • 10
  • I changed the body of template functions to the header, it didn't work. However, as an attempt, I also tried to change the normal functions to the header, and It worked. Why did I also have to replace the normal functions to the header? – André Machoski Jul 03 '20 at 20:32
  • @AndréMachoski apart from trivial errors (forgot to save the file and such), I'd suggest you wasn't using the right command to link (maybe missing arguments specifying all files needed), so adding them to the headers removed the need of linking. – Frazzo Jul 03 '20 at 22:00