[英]Using std::any_cast with decltype ... why does this code throw a bad any_cast?
[英]Why does an std::any_cast of a passed std::any inside a dlopen'd function raise an error
我正在玩c ++ 17和插件,我遇到了一個我無法解決的錯誤。 在下面的MWE中,我可以調用一個帶有std::any
的本地函數,當我嘗試讀取內容時,一切都按預期工作。 當我通過插件(dlopen)加載這個完全相同的函數時,它正確地看到了any上的類型,但它不能std::any_cast
內容。
在弄清楚導致此錯誤的原因時,將非常感謝任何幫助。
這是我的環境,MWE和產生的錯誤。
>> g++ --version
g++ (GCC) 7.1.1 20170526 (Red Hat 7.1.1-2)
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
>> scons --version
SCons by Steven Knight et al.:
script: v2.5.1.rel_2.5.1:3735:9dc6cee5c168[MODIFIED], 2016/11/03 14:02:02, by bdbaddog on mongodog
engine: v2.5.1.rel_2.5.1:3735:9dc6cee5c168[MODIFIED], 2016/11/03 14:02:02, by bdbaddog on mongodog
engine path: ['/usr/lib/scons/SCons']
Copyright (c) 2001 - 2016 The SCons Foundation
>> tree
.
├── SConstruct
└── src
├── main.cpp
├── plugin.cpp
└── SConscript
1 directory, 4 files
>> cat SConstruct
SConscript('src/SConscript', variant_dir='build', duplicate=0)
>> cat src/SConscript
env = Environment()
env.Append(CXXFLAGS=['-std=c++17'])
plugin = env.SharedLibrary('plugin', 'plugin.cpp')
Install('../lib', plugin)
driver_env = env.Clone()
driver_env.Append(LIBS=['dl', 'stdc++fs'])
driver = driver_env.Program('driver', 'main.cpp')
Install('../bin', driver)
>> cat src/plugin.cpp
#include <any>
#include <iostream>
using namespace std;
extern "C" {
int plugin(any& context) {
cout << " Inside Plugin" << endl;
cout << " Has Value? " << context.has_value() << endl;
cout << " Type Name: " << context.type().name() << endl;
cout << " Value: " << any_cast<double>(context) << endl;
}
}
>> cat src/main.cpp
#include <functional>
#include <iostream>
#include <stdexcept>
#include <any>
#include <experimental/filesystem>
#include <dlfcn.h>
using namespace std;
using namespace std::experimental;
function< void(any&) > loadplugin(string filename) {
function< void(any&) > plugin;
filesystem::path library_path(filename);
filesystem::path library_abspath = canonical(library_path);
void * libraryHandle = dlopen(library_abspath.c_str(), RTLD_NOW);
if (!libraryHandle) {
throw runtime_error("ERROR: Could not load the library");
}
plugin = (int(*) (any&))dlsym(libraryHandle, "plugin");
if (!plugin) {
throw runtime_error("ERROR: Could not load the plugin");
}
return plugin;
}
int local(any& context) {
cout << " Inside Local" << endl;
cout << " Has Value? " << context.has_value() << endl;
cout << " Type Name: " << context.type().name() << endl;
cout << " Value: " << any_cast<double>(context) << endl;
}
int main(int argc, char* argv[]) {
cout << " Resolving Paths..." << endl;
filesystem::path full_path = filesystem::system_complete( argv[0] ).parent_path();
filesystem::path plugin_path(full_path/".."/"lib"/"libplugin.so");
cout << " Creating Context..." << endl;
any context(.1);
cout << " Loading Plugin..." << endl;
function<void(any&) > plugin = loadplugin(plugin_path.string());
cout << " Calling Local..." << endl;
local(context);
cout << " Calling Plugin..." << endl;
plugin(context);
}
>> scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o build/main.o -c -std=c++17 src/main.cpp
g++ -o build/driver build/main.o -ldl -lstdc++fs
Install file: "build/driver" as "bin/driver"
g++ -o build/plugin.os -c -std=c++17 -fPIC src/plugin.cpp
g++ -o build/libplugin.so -shared build/plugin.os
Install file: "build/libplugin.so" as "lib/libplugin.so"
scons: done building targets.
>> tree
.
├── bin
│ └── driver
├── build
│ ├── driver
│ ├── libplugin.so
│ ├── main.o
│ └── plugin.os
├── lib
│ └── libplugin.so
├── SConstruct
└── src
├── main.cpp
├── plugin.cpp
└── SConscript
4 directories, 10 files
>> bin/driver
Resolving Paths...
Creating Context...
Loading Plugin...
Calling Local...
Inside Local
Has Value? 1
Type Name: d
Value: 0.1
Calling Plugin...
Inside Plugin
Has Value? 1
Type Name: d
terminate called after throwing an instance of 'std::bad_any_cast'
what(): bad any_cast
Value: Aborted (core dumped)
libstdc ++的any
依賴於同一模板實例化的地址在程序中是相同的,這意味着如果你使用dlopen
你需要采取預防措施 :
編譯器必須在任何需要它們存在的轉換單元中發出[具有模糊鏈接的對象,如模板實例化],然后依賴鏈接和加載過程來確保它們中只有一個在最終可執行文件中處於活動狀態。 使用靜態鏈接,所有這些符號在鏈接時解析,但通過動態鏈接,在加載時進一步解析。 您必須確保共享庫中的對象是針對可執行文件和其他共享庫中的對象進行解析的。
- 對於與共享庫鏈接的程序,不需要額外的預防措施。
- 您無法使用
-Bsymbolic
選項創建共享庫,因為這會阻止上述解決方案。- 如果使用
dlopen
從共享庫中顯式加載代碼,則必須執行多項操作。 首先,通過將可執行文件與-E
標志鏈接起來,從可執行文件中導出全局符號(如果從編譯器驅動程序(g ++)以通常的方式調用鏈接器,則必須將其指定為-Wl,-E
)。 您還必須通過向dlopen
提供RTLD_GLOBAL
標志,使加載庫中的外部符號可用於后續庫。 符號解析可以是立即的或懶惰的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.