What is the cause of undefined references? (NEWBIE)

In Eclipse I developed a C++ - client for Basex (an Open source XML-database) and it is my intention to deploy that client as shared library. A simple CMakelists.txt is enough to build an so. However, according to this article “(Modern CMake tutorial for C++ library developers | by Anton Pantyukhin | Medium)” , it takes more to really use an so effectively.
Based on the accompagning sample code I converted my code to this environment.

  • In CMakeLists.txt, I replaced all occurrences of mylib and MYLIB with BaseXCpp and BASEXCPP, respectively.
  • In the public header BasexCpp.h, I changed all class definitions to “class BASEXCPP_EXPORT …”.

And after adjusting some paths in header files, the command 'cmake -S . -B Build/Debug/ --fresh -DCMAKE_INSTALL_PREFIX=/home//lib -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -G “Ninja” results in so-files in the “Build/Debug” directory.

However, when I use the following CMakeLists.txt to link the just created so to a test file, I get an error message for each method that is called in the so (example: /usr/bin/ld: libBasexTest.cpp:(.text+0x34a): undefined reference to `BasexCpp::BasexClient::getStatusabi:cxx11’)

cmake_minimum_required(VERSION 3.14)
project(lib-Test LANGUAGES CXX)

include("../../cmake/utils.cmake")
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" is_top_level)

if(is_top_level)
    find_package(BasexCpp REQUIRED)
endif()

set(sources libBasexTest.cpp)
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${sources})

add_executable(lib-Test)
target_sources(lib-Test PRIVATE ${sources})
target_link_libraries(lib-Test PRIVATE BasexCpp::BasexCpp)

if(NOT is_top_level)
    win_copy_deps_to_target_dir(lib-Test BasexCpp::BasexCpp)
endif()

This is the code in compile_commands.json that is related to the test application. It seems that libBasexTest.cpp.o is not linked to the shared library.

{
  "directory": "/home/<user>/Thuis/C_Test2/libBasexCpp/Build/Debug",
  "command": "/usr/lib64/ccache/c++ -I/home/<user>/Thuis/C_Test2/libBasexCpp/include -I/home/bengbers/Thuis/C_Test2/libBasexCpp/Build/Debug/include -fvisibility=hidden -fvisibility-inlines-hidden -o examples/Test/CMakeFiles/lib-Test.dir/libBasexTest.cpp.o -c /home/bengbers/Thuis/C_Test2/libBasexCpp/examples/Test/libBasexTest.cpp",
  "file": "/home/<user>/Thuis/C_Test2/libBasexCpp/examples/Test/libBasexTest.cpp",
  "output": "examples/Test/CMakeFiles/lib-Test.dir/libBasexTest.cpp.o"
}

I have 2 questions:

  1. Is adding BASEXCPP_EXPORT to the class defiitions in BasexCpp.h the correct way to export symbols?
  2. Should I add something to CMakeLists,txt to make sure libBasexTest.o is linked to the shared library?

Ben

In another directory I manually compiled the test file (libBasexCpp.cpp) copied the shared library object files to the desired destination.
The command:
libBasexCpp]$ g++ -L ~/lib/lib64 -Wall -o libTest libBasexTest.o -lBasexCpp

produced the same errors so it is still my guess that the symbols are not exported in the proper way.

The file ‘export_shared.h’ is generated and contains only some macros

This the example header file from the article:

#pragma once

#include <mylib/export.h>

namespace mylib {

MYLIB_EXPORT int add(int a, int b);
}

It exports single symbols. In my case I want to export (public) class methods.

#pragma once

#ifndef LIBBASEXCPP_H
#define LIBBASEXCPP_H

#include <string>
#include <vector>
#include <list>
#include <cstddef>
#include <ostream>
#include <iostream>
#include <sys/socket.h>

#include <BasexCpp/export.h>

typedef
  std::vector <
  std::vector <
  std::byte >>
  VectOfByteVect;
typedef
  std::vector <
  std::byte >
  ByteVect;

std::vector < std::byte > getBytes(std::string const &s);
std::string getChar(ByteVect const &b);
std::string pathExpander(const std::string & s, int flags = 0);
std::ostream & debug_dump(const ByteVect & str, std::ostream & stm =
			  std::cout);
std::ostream & debug_dump(const std::string & str, std::ostream & stm =
			  std::cout);

namespace BasexCpp {

class BASEXCPP_EXPORT BasexSocket
{
public:
  BasexSocket(const std::string, const std::string,
				 const std::string, const std::string);
  ~BasexSocket();
  BasexSocket(const BasexSocket &) = delete;
  BasexSocket & operator=(const BasexSocket &) = delete;

  int basex_status();	// moet nog geimplementeerd worden

  int get_Socket();
  bool wait();

  int readSocket(std::string & sockResponseString);
  int writeData(const std::string & input);
  int writeData(const std::vector < std::byte > &input);
  int writeData(const char *input);
  int writeByte(unsigned char rawNum);
  std::vector < std::byte > decode(const std::vector <
						      std::byte > &encoded);
};

class BASEXCPP_EXPORT Base
{
public:
  Base(const std::string &, const std::string &,
			  const std::string &, const std::string &);
  Base(BasexSocket * socket);
  virtual ~ Base();

  Base & addVoid(std::string Add,
				    std::vector < std::byte > &To);
  Base & addVoid(std::vector < std::byte > Add,
				    std::vector < std::byte > &To);
  Base & pushByte(unsigned char Oct,
				     std::vector < std::byte > &To);
  Base & handShake(std::vector < std::byte > Input,
				      std::vector < std::byte > &Result);

  BasexSocket *Socket;
  BasexSocket *getSocket();
};

class BASEXCPP_EXPORT ResponseObj
{
public:
  ResponseObj();
  ResponseObj(const std::string &, ByteVect &);
  virtual ~ ResponseObj();

  void setValues(const std::string &, ByteVect &);

  std::string getStatus();
  std::string getCaller()
  {
    return CallFunction.c_str();
  };				// Name from 'calling' function
  ByteVect getResultBytes()
  {
    return ResultBytes;
  };				// Result without inserted \FF
  std::string getInfo();	// info or error message
  ByteVect getResult();	// Result from Command
  std::string getResultString();	// Result converted to string

  std::byte StatusByte;
  std::string CallFunction;
  ByteVect ResultBytes;
  VectOfByteVect Splitted;

  ByteVect decode(const ByteVect & encoded);
  static VectOfByteVect splitter(ByteVect & BytesVector,
						    std::byte By =
						    (std::byte) 0x00);
};

class BASEXCPP_EXPORT QueryObject:public Base
{
public:
  QueryObject(const std::string &, BasexSocket *);
    virtual ~ QueryObject();

  void Close();
  void Bind(const std::string & name,
			       const std::string & value,
			       const std::string & type = "");
  void Bind(const std::string & name,
			       const std::list < std::string > &valueList,
			       const std::string & type = "");
  void Bind(const std::string & name,
			       const std::list < std::string > &valueList,
			       const std::list < std::string > &typeList);
  ByteVect Next();
  bool More();
  void Exec();
  void Info();
  void Options();
  void Context(const std::string & value,
				  const std::string & type = "");
  void Updating();
  void Full();

  std::string queryString
  {
  };
  std::string getQueryString();
  std::string getStatus();
  ByteVect getResultBytes();
  ByteVect getResult();
  std::string getResultString();
  std::string asString(const ByteVect & vect);

  ResponseObj Response;
  VectOfByteVect *Cache
  {
  nullptr};
};

class BASEXCPP_EXPORT BasexClient:public Base
{
public:
  BasexClient(const std::string &, const std::string &,
				 const std::string &, const std::string &);
  virtual ~ BasexClient();
  void Execute(const std::string & command);
  void Create(const std::string & dbName,
				 const std::string & content = "");
  void Add(const std::string & path,
			      const std::string & input);
  void Put(const std::string & path,
			      const std::string & input);
  void PutBinary(const std::string & path,
				    const std::vector < std::byte > &input);
  QueryObject *Query(const std::string & query);
  QueryObject *Query(const std::string & query,
					BasexSocket * socket);
  std::string getCaller();
  std::string getInfo();
  std::string getStatus();
  std::vector < std::byte > getResultBytes();
    std::vector < std::byte > getResult();
};

} // namespace libBasexCpp
#endif // LIBBASEXCPP_H

Is this the correct method to export the public symbols for a shared library?