What is / Is there a relation between exporting symbols and public methods and shared libraries?

I started with a simple CMakeLists.txt for creating a shared library. After developing working source code in Eclipse, I copied that source code to a new directory, generated a shared library there and then linked the library to a test file. In Eclipse, there were no error messages. However, the test application showed that there was a memory leak somewhere. Since I did not succeed in creating debug information
I then started searching on internet and in doing so I came across this article “Modern CMake tutorial for C++ library developers”.
After I adapted the attached scripts to my application a shared library is now created. But when I try to link it to a test application I get ‘undefined reference to …’ error messages for every call to a public method.

The article does talk about exporting symbols but does not talk about exporting public methods. I have studied the CMake documentation as best I can but have not found any useful information about exporting symbols. This leads me to the following questions:

  • Are symbols similar to public methods?
  • If so, in what file and in what way should I define those symbols?

I tried the following way in the public header but it doesn’t work.
My CMakeLists.txt and part of the public header are below.

Ben

Public BasexCpp.h

#pragma once

#ifndef BASEXCPP_H
#define BASEXCPP_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);
};
...
...

CMakeLists.txt

cmake_minimum_required(VERSION 3.14)
project(BasexCpp
    VERSION 1.0.0
    DESCRIPTION "Template for C++ library built with CMake"
    LANGUAGES CXX)
set(CMAKE_INSTALL_PREFIX)

#----------------------------------------------------------------------------------------------------------------------
# general settings and options
#----------------------------------------------------------------------------------------------------------------------

include(cmake/utils.cmake)
include(GNUInstallDirs)

string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" is_top_level)

# BASEXCPP_SHARED_LIBS option (undefined by default) can be used to force shared/static build
option(BASEXCPP_SHARED_LIBS "Build BasexCpp shared lib" ON)
option(BASEXCPP_BUILD_TESTS "Build BasexCpp tests" OFF)
option(BASEXCPP_BUILD_EXAMPLES "Build BasexCpp examples" ON)
option(BASEXCPP_BUILD_DOCS "Build BasexCpp documentation" OFF)
option(BASEXCPP_INSTALL "Generate target for installing BasexCpp" ${is_top_level})
set_if_undefined(BASEXCPP_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/BasexCpp" CACHE STRING
    "Install path for BasexCpp package-related CMake files")

if(DEFINED BASEXCPP_SHARED_LIBS)
    set(BUILD_SHARED_LIBS ${BASEXCPP_SHARED_LIBS})
endif()

if(NOT DEFINED CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()

set_if_undefined(CMAKE_CXX_VISIBILITY_PRESET hidden)
set_if_undefined(CMAKE_VISIBILITY_INLINES_HIDDEN ON)

add_library(BasexCpp) # initialized below
add_library(BasexCpp::BasexCpp ALIAS BasexCpp)

#----------------------------------------------------------------------------------------------------------------------
# BasexCpp dependencies
#----------------------------------------------------------------------------------------------------------------------

include(FindPkgConfig)
pkg_check_modules(CURLPP REQUIRED curlpp)
include(FindCURL)
find_package(CURL REQUIRED)
if(NOT CURL_FOUND)
  message(FATAL_ERROR "Could not find CURL")
endif()

#----------------------------------------------------------------------------------------------------------------------
# BasexCpp sources
#----------------------------------------------------------------------------------------------------------------------

include(GenerateExportHeader)
set(export_file_name "export_shared.h")

if(NOT BUILD_SHARED_LIBS)
    set(export_file_name "export_static.h")
endif()

generate_export_header(BasexCpp EXPORT_FILE_NAME include/BasexCpp/${export_file_name})

target_include_directories(BasexCpp PRIVATE src)

set(sources
    include/BasexCpp/export.h
    include/BasexCpp/BasexCpp.h
    include/BasexCpp/export.h
    include/BasexCpp/BasexCpp.h
    src/Base.cpp
    src/BasexClient.cpp
    src/BasexSocket.cpp
    src/ClientUtils.cpp
    src/QueryObject.cpp
    src/ResponseObj.cpp)
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${sources})

#----------------------------------------------------------------------------------------------------------------------
# BasexCpp target
#----------------------------------------------------------------------------------------------------------------------

include(CMakePackageConfigHelpers)

target_sources(BasexCpp PRIVATE ${sources})
target_compile_definitions(BasexCpp PUBLIC "$<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:BASEXCPP_STATIC_DEFINE>")

target_include_directories(BasexCpp
    PUBLIC
        "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
        "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")

set_target_properties(BasexCpp PROPERTIES
    SOVERSION ${PROJECT_VERSION_MAJOR}
    VERSION ${PROJECT_VERSION})

if(BASEXCPP_INSTALL AND NOT CMAKE_SKIP_INSTALL_RULES)
    configure_package_config_file(cmake/BasexCpp-config.cmake.in BasexCpp-config.cmake
        INSTALL_DESTINATION "${BASEXCPP_INSTALL_CMAKEDIR}")
    message("Installeer naar ${BASEXCPP_INSTALL_CMAKEDIR}")
    message("Exporteer op basis van ${export_file_name}")

    write_basic_package_version_file(BasexCpp-config-version.cmake
        COMPATIBILITY SameMajorVersion)

    install(TARGETS BasexCpp EXPORT BasexCpp_export
        RUNTIME COMPONENT BasexCpp
        LIBRARY COMPONENT BasexCpp NAMELINK_COMPONENT BasexCpp-dev
        ARCHIVE COMPONENT BasexCpp-dev
        INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
    install(DIRECTORY include/
        TYPE INCLUDE
        COMPONENT BasexCpp-dev)
    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/BasexCpp/${export_file_name}"
        COMPONENT BasexCpp-dev
        DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/BasexCpp")

    set(targets_file "BasexCpp-shared-targets.cmake")

    if(NOT BUILD_SHARED_LIBS)
        set(targets_file "BasexCpp-static-targets.cmake")
    endif()

    install(EXPORT BasexCpp_export
        COMPONENT BasexCpp-dev
        FILE "${targets_file}"
        DESTINATION "${BASEXCPP_INSTALL_CMAKEDIR}"
        NAMESPACE BasexCpp::)

    install(FILES
        "${CMAKE_CURRENT_BINARY_DIR}/BasexCpp-config.cmake"
        "${CMAKE_CURRENT_BINARY_DIR}/BasexCpp-config-version.cmake"
        COMPONENT BasexCpp-dev
        DESTINATION "${BASEXCPP_INSTALL_CMAKEDIR}")

    if(MSVC)
        set(pdb_file "")
        set(pdb_file_destination "")

        if(BUILD_SHARED_LIBS)
            set(pdb_file "$<TARGET_PDB_FILE:BasexCpp>")
            set(pdb_file_destination "${CMAKE_INSTALL_BINDIR}")
        else()
            # TARGET_PDB_FILE does not work for pdb file generated for static library build, determining it manually
            set(pdb_file "$<TARGET_FILE_DIR:BasexCpp>/$<TARGET_FILE_PREFIX:BasexCpp>$<TARGET_FILE_BASE_NAME:BasexCpp>.pdb")
            set(pdb_file_destination "${CMAKE_INSTALL_LIBDIR}")
        endif()

        install(FILES "${pdb_file}"
            COMPONENT BasexCpp-dev
            CONFIGURATIONS Debug RelWithDebInfo
            DESTINATION "${pdb_file_destination}"
            OPTIONAL)
    endif()
endif()

#----------------------------------------------------------------------------------------------------------------------
# other targets
#----------------------------------------------------------------------------------------------------------------------

if(BASEXCPP_BUILD_TESTS)
    enable_testing()
    add_subdirectory(tests)
endif()

if(BASEXCPP_BUILD_EXAMPLES)
    add_subdirectory(examples)
endif()

if(BASEXCPP_BUILD_DOCS)
    find_package(Doxygen REQUIRED)
    doxygen_add_docs(docs include)
endif()

perhaps you should note this CppCon 2019: Deep CMake For Library Authors

This is telling CMake add a flag to the compile commands that make all methods hidden or private by default. For example, in Clang this is -fvisibility=hidden.

In order for a method to be available for (dynamic) linking, it must be visible. Again, depending on the compiler, this usually involves adding an attribute to the function to expose it. Lots of time people will make a macro such as DLL_EXPORT that gets expanded into the appropriate attribute for that compiler. In Clang it could be expanded as

__attribute__((visibility(default))) void my_exported_function(int value) {
   ...
}

The article https://medium.com/@pananton/modern-cmake-tutorial-for-c-library-developers-12f3bf57dc87 came with a template. And since my code compiled without errors in Eclipse, I thought it was safe to copy that code to the template.
When using the template the code also compiled without errors and a .so-file was created. When using that .so however, a lot of errors were produced.
The template contained also a file CMakePresets.json. That file contained the line "CMAKE_CXX_FLAGS": "-std=c++20 -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wsign-conversion -Wcast-align -Wcast-qual -Wnull-dereference -Woverloaded-virtual -Wformat=2 -Werror". It was only when I started using presets that compilation in CMake failed.
After adding that line to the CmakeLists.txt in the Eclipse project, compilation there also failed.
After fixing all those errors, I could finally use the template without errors.