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
用于更大的块,您需要在其中显示所有内容。 (我用这个有效地装箱了我所有的包含文件......可能很粗糙,但很有效)
我不确定哪里出错了,但无论如何,这就是我所看到的:
假设我有一个定义如下的库:
# 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
用于更大的块,您需要在其中显示所有内容。 (我用这个有效地装箱了我所有的包含文件......可能很粗糙,但很有效)