pybind11 类型作为库中的函数参数,在链接到该库的应用程序中产生编译错误

pybind11 type as function parameters in library yielding compile error in application linking against that library

我不确定哪里出错了,但无论如何,这就是我所看到的:

假设我有一个定义如下的库:

# minimal-lib/CMakeLists.txt

cmake_minimum_required(VERSION 3.14)

project(minimallib
    LANGUAGES CXX
    VERSION 1.0.0
    DESCRIPTION "minimal library")
include(GNUInstallDirs)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CPP_COMPILER clang++-13)

# get rid of the pybind11 warning spam
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-Wno-attributes HAVE_ATTRIBUTES)
if (HAVE_ATTRIBUTES)
    add_compile_options(-Wno-attributes)
endif()


IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
SET(CMAKE_INSTALL_PREFIX /opt/experiment CACHE PATH "comment" FORCE)
ENDIF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)

set(
    PSOURCES
        src/fun.cpp
)
set(
    PHEADERS
        include/fun.hpp
)

find_package(pybind11 REQUIRED)

add_library(${PROJECT_NAME} SHARED
    ${PSOURCES}
    ${PHEADERS}
)

set_target_properties(${PROJECT_NAME}
    PROPERTIES
        VERSION 1.0.0
        SOVERSION 1.0.0
        LINKER_LANGUAGE CXX
        MAP_IMPORTED_CONFIG_COVERAGE "DEBUG"
)
target_include_directories(${PROJECT_NAME}
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:include>
        PRIVATE src)
target_link_libraries(${PROJECT_NAME} PUBLIC pybind11::embed)

install(
    TARGETS ${PROJECT_NAME}
    EXPORT ${PROJECT_NAME}Config
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(
    DIRECTORY include/
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
)
export(
    TARGETS ${PROJECT_NAME}
    FILE ${PROJECT_NAME}Config.cmake
    NAMESPACE ${PROJECT_NAME}::
    FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
)
install(
    EXPORT ${PROJECT_NAME}Config
    DESTINATION "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake"
)
//minimal-lib/include/fun.hpp

#ifndef FUN_HPP
#define FUN_HPP

#include <pybind11/pybind11.h>
#include <optional>
#include <string>

namespace minimallib
{
void write(
        const std::optional<std::string> &file, 
        const pybind11::args &args = pybind11::none(), 
        const pybind11::kwargs &kwargs = pybind11::none());
}//namespace minimallib
#endif //FUN_HPP
//minimal-lib/src/fun.cpp

#include <iostream>
#include "fun.hpp"

void minimallib::write(
        const std::optional<std::string> &file, 
        const pybind11::args &args, 
        const pybind11::kwargs &kwargs)
{
    std::cout << "I should do something more useful." << std::endl;
}

以及一个使用该库的应用程序项目,如下所示:

# rms/CMakeLists.txt
cmake_minimum_required(VERSION 3.16.0)

project(minimalapp
    LANGUAGES CXX
    VERSION 1.0.0)

include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
  SET(CMAKE_INSTALL_PREFIX /opt/experiment CACHE PATH "comment" FORCE)
ENDIF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)

list(APPEND CMAKE_PREFIX_PATH "/opt/experiment/")

find_package(pybind11 REQUIRED)
find_package(minimallib REQUIRED)

set(
    PSOURCES
        src/main.cpp
)

set(
    MAIN_HEADERS

)

add_executable(${PROJECT_NAME}
    ${PHEADERS}
    ${PSOURCES}
)

set_target_properties(${PROJECT_NAME}
    PROPERTIES
        VERSION 1.0.0
        MAP_IMPORTED_CONFIG_COVERAGE "DEBUG")

set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX)

target_include_directories(${PROJECT_NAME}
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:include>
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/src)

target_link_libraries(${PROJECT_NAME} PRIVATE pybind11::embed)
target_link_libraries(${PROJECT_NAME} PRIVATE minimallib)


install(TARGETS ${PROJECT_NAME}
    EXPORT ${PROJECT_NAME}Targets
    RUNTIME DESTINATION bin)
#include <minimallib/fun.hpp>

int main()
{
    minimallib::write("foobar.txt");
    return 0;
}

库编译并 links 没有抱怨。 在任何情况下,link使用该库的应用程序都会失败并出现如下错误:

FAILED: minimalapp-1.0.0 
: && /usr/bin/c++   CMakeFiles/minimalapp.dir/src/main.cpp.o -o minimalapp-1.0.0  -Wl,-rpath,/opt/experiment/lib:  /opt/experiment/lib/libminimallib.so.1.0.0  /usr/lib/x86_64-linux-gnu/libpython3.9.so && :
/usr/bin/ld: CMakeFiles/minimalapp.dir/src/main.cpp.o: in function `main':
main.cpp:(.text+0x1bc): undefined reference to `minimallib::write(std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&, pybind11::args const&, pybind11::kwargs const&)'
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.

如果我从库中函数的签名中删除 pybind11::kwargs 和 pybind11::args,使用它的应用程序编译没有问题。

我对*.so 文件中包含的符号有些无知,但我还是看了一下,这是我观察到的。 情况1)签名中包含pybind11::args和pybind11::kwargs:用objdump -t可以看到相关符号,用objdump -T看不到。

0000000000006259 l     F .text  0000000000000046              _ZN10minimallib5writeERKSt8optionalINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEERKN8pybind114argsERKNSA_6kwargsE

情况2)不包括那些:用objdump -t和objdump -T都可以看到相关符号。我注意到它也被标记为 (g)lobal 而不是 (l)ocal,并且被标记为 (D)ynamic 而不是 ( )normal

0000000000006259 g    DF .text  000000000000003e  Base        _ZN10minimallib5writeERKSt8optionalINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE

这是否意味着相关符号对试图 link 库的应用程序不可见,还是我在追尾?

有没有人有任何见解或想法可以帮助我解决这个问题?

我找到了一个解决方案,尽管我仍然不知道为什么行为会改变它向函数添加一对参数的方式......我只能想象定义的库类型将可见性设置为隐藏或其他...

为了后代,对于不需要存在于 GCC/Clang 之外的库,可以在命名空间名称之后附加它 __attribute__ ((visibility("default"))) 要么 可以将 #pragma GCC visibility push(default) 和后置 #pragma GCC visibility pop 用于更大的块,您需要在其中显示所有内容。 (我用这个有效地装箱了我所有的包含文件......可能很粗糙,但很有效)