Почему std :: any_cast переданного std :: any внутри функции dlopen’d вызывает ошибку

Я играю с c ++ 17 и плагинами, и я столкнулся с ошибкой, которую не могу обойти. В следующем MWE я могу вызвать локальную функцию, которая принимает std::anyи все работает как положено, когда я пытаюсь прочитать содержимое. Когда я загружаю эту же функцию через плагин (dlopen), он правильно видит тип по любому, но не может 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)

3

Решение

libstdc ++ ‘s any полагается, что адрес одного и того же экземпляра шаблона внутри программы одинаков, а это означает, что вам нужно принять меры предосторожности, если вы используете dlopen:

Компилятор должен испускать [объекты с неопределенной связью, как шаблон
экземпляры] в любой единице перевода, которая требует их присутствия,
а затем полагаться на процесс связывания и загрузки, чтобы убедиться, что
только один из них активен в конечном исполняемом файле. Со статическим
связывание всех этих символов разрешается во время соединения, но с
динамическое связывание, дальнейшее разрешение происходит во время загрузки. Ты должен
убедитесь, что объекты в общей библиотеке разрешены
объекты в исполняемом файле и другие общие библиотеки.

  • Для программы, связанной с общей библиотекой, никаких дополнительных мер предосторожности не требуется.
  • Вы не можете создать общую библиотеку с -Bsymbolic вариант, так как это предотвращает разрешение, описанное выше.
  • Если вы используете dlopen чтобы явно загрузить код из разделяемой библиотеки, вы должны сделать несколько вещей. Сначала экспортируйте глобальные символы из
    исполняемый файл, связав его с -E флаг (вам нужно будет указать
    это как -Wl,-E если вы вызываете компоновщик в обычном порядке
    из драйвера компилятора, g ++). Вы также должны сделать внешний
    символы в загруженной библиотеке доступны для последующих библиотек
    предоставляя RTLD_GLOBAL флаг для dlopen, Разрешение символа
    может быть немедленным или ленивым.
6

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]