Я получаю разные результаты для одной и той же общей библиотеки, когда функция (из общей библиотеки) выполняется автономным исполняемым файлом и при использовании ctypes Python для вызова одной и той же функции. Разница была сужена до версии libm, используемой библиотекой. При использовании библиотеки из автономного исполняемого файла мы связываемся с openlibm, но когда библиотека связана с помощью ctypes, она связана с Libm Ubuntu. Как я могу заставить ctypes связать мою общую библиотеку с openlibm?
Заголовок разделяемой библиотеки (fft.h):
extern "C" {
void fft(const char* srcPath);
};
Реализация разделяемой библиотеки (fft.cpp):
#include <iostream>
#include <fstream>
#include <nlohmann/json.hpp>
#include <Eigen/Eigen>
#include <unsupported/Eigen/FFT>
void fft(const char* srcPath) {
std::cout.precision(17);
std::ifstream fs(srcPath, std::ofstream::in);
// Get length of file
fs.seekg(0, std::ifstream::end);
auto length = fs.tellg();
fs.seekg(0, std::ifstream::beg);
// Read the file
auto srcRaw = new char[length];
fs.read(srcRaw, length);
// Convert data to vector of doubles
nlohmann::json j = nlohmann::json::parse(srcRaw);
std::vector<double> srcDoubles = j;
int n = 65536;
Eigen::VectorXd srcEigen(n);
Eigen::VectorXcd dstEigen(n);
std::copy(srcDoubles.data(), srcDoubles.data() + srcDoubles.size(), srcEigen.data());
Eigen::FFT<double> fft;
fft.fwd(dstEigen, srcEigen);
std::vector<std::complex<double>> dstTmp(dstEigen.data(), dstEigen.data() + dstEigen.size());
for (size_t i = 0; i < dstTmp.size(); i++) {
std::cout << "Result[" << i << "] = " << dstTmp[i].real() << "+i*" << dstTmp[i].imag() << std::endl;
}
delete[] srcRaw;
}
Использование библиотеки из автономного исполняемого файла:
#include <fft.h>
int main(int argc, char** argv) {
fft("input_data");
}
Использование ctypes Python:
from ctypes import c_char_p, CDLL
class LibCFft(CDLL):
def __init__(self):
super(LibCFft, self).__init__("libexample.so")
self._addfunc('fft', [c_char_p], None)
def _addfunc(self, name, argtypes, restype):
attr = getattr(self, name)
attr.argtypes = argtypes
attr.restype = restype
return lambda x: x
class CFft(object):
def fft(self):
LibCFft().fft("input_data")
if __name__ == '__main__':
CFft().fft()
Сборка lib:
clang++ /opt/eigen-eigen-43d9075b23ef -isystem /opt/spdlog/include -isystem /opt/nlohmann/include -O3 -DNDEBUG -fPIC -std=gnu++14 -o CMakeFiles/example.dir/fft.cpp.o -c /home/user/fft.cpp
clang++ -fPIC -O3 -DNDEBUG -shared -Wl,-soname,libexample.so -o libbugatone.so CMakeFiles/example.dir/generated/fft.cpp.o -lopenlibm
Исполняемая сборка:
clang++ -O3 -DNDEBUG -latomic -nodefaultlibs -lopenlibm -lc -lc++ -lgcc -lgcc_s -lstdc++ -latomic -lm CMakeFiles/eigen_tester.dir/eigen_tester.cpp.o -o eigen_tester libexample.so -lopenlibm
Заранее спасибо за помощь.
Например, функция exp
ищется по ее имени. Обе библиотеки имеют свою собственную версию exp
, поэтому любой вызов exp
может быть перенаправлен на любую из них. При сборке моей библиотеки без-lopenlibm
я получаю те же результаты (например, результаты функции exp
), что и при использовании моей библиотеки ctypes. При сборке моей библиотеки с-lopenlibm
я получаю ожидаемые результаты при использовании собственного исполняемого файла, но получаю неверные результаты при использовании ctypes для загрузки этой библиотеки.
Попробуйте: ldd example.so
В любом случае ctypes не имеет отношения к линковке, он загружается динамически. Но опять же без mcve ([SO]: Как создать минимальный, полный и поддающийся проверке пример (mcve)) сложно сказать
Вывод ldd
- это то, что я ожидал (openlibm появляется перед libm). ctypes не связывается, но я предполагаю, что двоичный файл Python загружен с помощью libm, поэтому вызовы математических функций выполняются с использованием libm, а не openlibm (т.е. приоритет между изменениями библиотек).
Да, исполняемый файл Python (например, / usr / bin / python3) связан с libm.so, что означает, что он будет загружен при вызове ctypes. Один из способов убедиться, что вы вызываете правильную функцию, - это загрузить dll и получить указатель func вручную (dlopen
/ dlsym
) в вашем коде C++. Но я не знаю, насколько это масштабируемо.
У меня похожая проблема, мой .so
связан с статически с openlibm
, но он все еще использует функции из системного libm
при вызове из python
. :( У вас есть новости по этой проблеме, @Karpad? Не могли бы вы ее решить?
На этапе ссылки на исполняемый файл вы используете ОБА -lm и -lopenlibm, что, вероятно, не то, что вам нужно.
-nodefaultlibs отключает автоматическое включение всех стандартных библиотек, поэтому, если вы просто не включаете -lm, но сохраняете -lopenlibm, это, вероятно, должно делать то, что вы хотите.
Вы можете обновить LD_LIBRARY_PATH в своей среде, чтобы сначала указать на папку локальной библиотеки и сохранить там свой libopenlibm.so, а также создать на него мягкую ссылку: 'ln -s libopenlibm.so libm.so'. В большинстве случаев это приведет к тому, что любые общие объекты будут использовать это как замену обычному libm.so
Мой ответ напрямую применим к проблеме. Любой исполняемый файл, который связан с -nodefaultlibs и не имеет -lm в списке аргументов компоновщика, не будет связан с ним, и это то, что задает OP, насколько я понимаю вопрос, так что нет, я выиграл ' Я не смогу улучшить свой ответ.
@AlexeyPolyudov Обратите внимание, что разделяемая библиотека построена без -lm
. Только исполняемый файл собран с -lm
- и он работает, как ожидалось. Проблема заключается в использовании разделяемой библиотеки с ctypes, и в этом сценарии исполняемый файл вообще не используется.
Я понял, спасибо. Я обновил ответ, чтобы охватить и случай с общими объектами.
Я сомневаюсь, что две библиотеки не совпадают, поскольку их имена различаются (libm.so *, против.libopenlibm.so *). Поэтому, когда загружается libexample.so (независимо от того, откуда), загрузчик автоматически пытается загрузить libopenlibm.so *. Может ваш вывод неверен? Не могли бы вы поделиться некоторыми подробностями (например, тестами и т. д.)?