当带有 C++ 的库时,动态转换返回 null python扩展在RHEL5上用作插件

发布于 2024-10-19 19:50:45 字数 3252 浏览 3 评论 0原文

我有一个带有 C++ python 扩展的库(C++ 调用 python,而 python 又调用 C++),使用 boost::python 和 python 库(这很混乱,但其中很多是遗留的),在独立测试时可以正常工作。特别是,某个dynamic_cast可以正常工作。

但是,当使用 gcc 4.1.2 和外部应用程序将该库打包用作 RHEL5 上的插件时,dynamic_cast 返回 NULL,导致应用程序无法按预期工作。在 Windows 上(使用 Visual Studio 2005 和 2008 测试 Vista 64 位)它运行良好。例如,当我使用 ddd 进行调试时,我可以看到转换之前的指针具有正确的 type_name (我想,像往常一样被编译器稍微破坏了?)。这里的任何具体调试技巧也会有所帮助。

一个reinterpret_cast解决了这个问题。虽然这肯定会受到阻碍,但我不知道如何继续,尤其是。因为这可能是由于外部应用程序的问题造成的。这是一个令人费解的混乱,几乎看起来毫无用处,但如果它可以帮助这里是一些示例代码。以下 C++ 代码片段创建一个“smart_handle”来对存储在字符串“input”中的某些 python 命令进行排队。字符串 IMPORT 导入由 boost::python::exec(..) 在函数调用 py_api::execute_py_command 中调用的一些函数的位置和定义:

boost::shared_ptr<my_base_class> 
         processor(new my_derived_class());
std::map<std::string, smart_handle> context;
context.insert(std::make_pair<std::string, smart_handle>("default_queue", 
               make_smart_handle(processor)));
const std::string py_command =
       IMPORT + 
       "namesp.dialects.cpython.set_command_queue('default', default_queue)\n" + 
       input;
if( !py_api::execute_py_command(py_command, context) ) {
  return false; 
}

make_smart_handle 定义为:

template <typename type_t> 
const smart_handle make_smart_handle(const boost::shared_ptr<type_t>& ptr) {
   if( !ptr ) {
      return smart_handle();
   }
   return smart_handle(new detail::smart_handle_weak_impl<type_t>(ptr));
}

函数 set_command_queue 在 python __init__ 中定义。 py as:

import func1 
import func2
import func3
import func4

COMMAND_QUEUE_MAP = {}
def set_command_queue(queue_name, object):
COMMAND_QUEUE_MAP[queue_name] = object
def get_command_queue(queue_name = 'default'):
return COMMAND_QUEUE_MAP[queue_name]

现在,在单独的 python 文件中定义的实际 python 函数 func1、func2 等调用命名空间“namesp”下定义的 C++ 函数。这些 C++ 函数的第一行是将“smart_handle”恢复到“队列”

boost::shared_ptr<my_base_class> queue = smart_handle_cast<my_base_class>(handle).lock();

: 。使用reinterpret_cast 可以使其正常工作。 smart_handle_cast 返回一个 const boost::weak_ptr。对于那些感兴趣的人,这里是 smart_handle_cast<..>() 函数的定义:

template <typename type_t> 
const boost::weak_ptr<type_t> smart_handle_cast(const smart_handle& handle, bool 
      throw_if_failure) {
   if( !handle.is_valid() ) {
       if( throw_if_failure ) {
          throw smart_handle::bad_handle("Bad handle, attempting to access an invalid 
                                          handle");
       }
       //-No throw version returns a non-initialized weak pointer
       return boost::weak_ptr<type_t>();
   }
   //-This line fails at run time and returns null.
   const detail::smart_handle_weak_impl<type_t>* casted = dynamic_cast<const 
                   detail::smart_handle_weak_impl<type_t>* >(handle.impl());
   if( !casted ) {
       if( throw_if_failure ) {
           throw smart_handle::bad_handle_cast("Bad handle cast, attempting to \
              convert to incorrect pointee type");
       }
       //-No throw version returns a non-initialized weak pointer
       return boost::weak_ptr<type_t>();
   }
   return casted->pointee;
}

I have a library with C++ python extensions (C++ calls python which in turn calls C++) using boost::python and python libraries (this is messy, but a lot of it is legacy) which when tested standalone works correctly. In particular, a certain dynamic_cast works correctly.

But when the library is packaged for use as a plugin on RHEL5 using gcc 4.1.2 with an external application, the dynamic_cast returns NULL resulting in the application not working as expected. On Windows (tested Vista 64 bit using Visual Studio 2005 and 2008) it works fine. When I debugged using ddd for instance, I am able to see that the pointer before casting has the right type_name (slightly mangled by compiler as is usual, I suppose?). Any specific debugging tips here will also be of help.

A reinterpret_cast solved the problem. While this will be certainly baulked at, I am at a loss about how to proceed, esp. since this could be due to issues with the external app. It is a convoluted mess and almost seems futile, but if it can help here is some sample code. The following C++ snippet creates a "smart_handle" to queue certain python commands stored in string "input". The string IMPORT imports locations and definitions of some functions that are called by boost::python::exec(..) in the function call py_api::execute_py_command:

boost::shared_ptr<my_base_class> 
         processor(new my_derived_class());
std::map<std::string, smart_handle> context;
context.insert(std::make_pair<std::string, smart_handle>("default_queue", 
               make_smart_handle(processor)));
const std::string py_command =
       IMPORT + 
       "namesp.dialects.cpython.set_command_queue('default', default_queue)\n" + 
       input;
if( !py_api::execute_py_command(py_command, context) ) {
  return false; 
}

The make_smart_handle is defined as:

template <typename type_t> 
const smart_handle make_smart_handle(const boost::shared_ptr<type_t>& ptr) {
   if( !ptr ) {
      return smart_handle();
   }
   return smart_handle(new detail::smart_handle_weak_impl<type_t>(ptr));
}

The function set_command_queue is defined in a python __init__.py as:

import func1 
import func2
import func3
import func4

COMMAND_QUEUE_MAP = {}
def set_command_queue(queue_name, object):
COMMAND_QUEUE_MAP[queue_name] = object
def get_command_queue(queue_name = 'default'):
return COMMAND_QUEUE_MAP[queue_name]

Now the actual python functions func1, func2, etc. defined in separate python files calls C++ functions defined under namespace "namesp". The very first line of these C++ functions is to recover the "smart_handle" to the "queue" by:

boost::shared_ptr<my_base_class> queue = smart_handle_cast<my_base_class>(handle).lock();

It is in the above function smart_handle_cast that the dynamic_cast is used which returns NULL, when the library is used as a plugin in an external app. Using reinterpret_cast allows it to work correctly. The smart_handle_cast returns a const boost::weak_ptr. For those interested, here is the defintion of the smart_handle_cast<..>() function:

template <typename type_t> 
const boost::weak_ptr<type_t> smart_handle_cast(const smart_handle& handle, bool 
      throw_if_failure) {
   if( !handle.is_valid() ) {
       if( throw_if_failure ) {
          throw smart_handle::bad_handle("Bad handle, attempting to access an invalid 
                                          handle");
       }
       //-No throw version returns a non-initialized weak pointer
       return boost::weak_ptr<type_t>();
   }
   //-This line fails at run time and returns null.
   const detail::smart_handle_weak_impl<type_t>* casted = dynamic_cast<const 
                   detail::smart_handle_weak_impl<type_t>* >(handle.impl());
   if( !casted ) {
       if( throw_if_failure ) {
           throw smart_handle::bad_handle_cast("Bad handle cast, attempting to \
              convert to incorrect pointee type");
       }
       //-No throw version returns a non-initialized weak pointer
       return boost::weak_ptr<type_t>();
   }
   return casted->pointee;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

近箐 2024-10-26 19:50:45

看看类似问题GCC 常见问题解答

如果使用 dlopen 从共享库显式加载代码,则必须执行几项操作。首先,通过将可执行文件与“-E”标志链接来导出全局符号(如果您以通常方式从编译器驱动程序 g++ 调用链接器,则必须将其指定为“-Wl,-E”) 。您还必须通过向 dlopen 提供 RTLD_GLOBAL 标志,使加载的库中的外部符号可用于后续库。符号解析可以是立即的或惰性的。

Take a look at similar question and GCC FAQ

If you use dlopen to explicitly load code from a shared library, you must do several things. First, export global symbols from the executable by linking it with the "-E" flag (you will have to specify this as "-Wl,-E" if you are invoking the linker in the usual manner from the compiler driver, g++). You must also make the external symbols in the loaded library available for subsequent libraries by providing the RTLD_GLOBAL flag to dlopen. The symbol resolution can be immediate or lazy.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文