我希望调用第 3 方库函数,该函数接受 aconst char* const*
作为参数类型:-
class Arguments
{
public:
Arguments(int argc, const char* const* p_argv = 0);
private:
deque<std::string> m_Args;
}
在库实现中,传递的对象通常是指向char** argv
. 然后将字符串缓存在私有成员变量 ( m_Args
) 中,因此我认为其中的指针p_argv
只需要在构造期间有效。
主要问题:我怎样才能或应该创建该const char* const* p_argv
变量?
更多解释
我正在尝试传递从其他数组类型(Boost Python list
s 和tuple
s)转换而来的对象,并试图找到一种安全(RAII)的方式来做到这一点。
我将在我的代码中的多个不同位置执行此操作,因此我尝试实现可以为我进行类型转换的可重用函数。例如
// File : converter.hpp
///! ConvertPyListToCharArrayPointer
///
/// Convert a Python list to const char* const*.
/// \param list - Python list of strings to convert. [in]
/// \param pargv - array of const char pointers. [in/out]
/// \returns - const pointer to first element in const char* array.
const char* const* ConvertPyListToCharArrayPointer(
const boost::python::list& list, const char** pargv);
我希望pargv
可以在堆栈上创建一个函数,然后调用ConvertPyListToCharArrayPointer
和store_arguments
。但是,对代码的单元测试表明,当从Arguments
类中恢复时,存储的字符串变得无效。(这是有道理的;我为指针数组分配存储空间,但不为这些指针指向的字符串分配存储空间)。
转换器实现
最初的ConvertPyListToCharArrayPointer
实现是:-
在堆栈上
// File : converter.cpp
const char* const* ConvertPyListToCharArrayPointer(
const boost::python::list& list, const char** pargv)
{
boost::python::stl_input_iterator<std::string> cur_key(list), end;
std::string temp_s;
int i=0;
while (cur_key != end)
{
// Get current arg as std::string
temp_s = (*cur_key++);
// Save string as a const char*, in pargv[i]
pargv[i++] = temp_s.c_str();
}
pargv[i] = NULL;
// Validation code...
const char* const* m_argv = &pargv[0];
return m_argv;
}
为了确切了解发生了什么,我部署了大量打印语句。在它说的地方Validation code...
,我目前有: -
printf("returning address (pargv[0]) of %s (%p)\n", pargv[0], &pargv[0]);
printf("pargv[1] of %s (%p)\n", pargv[1], &pargv[1]);
printf("pargv[2] of %s (%p)\n", pargv[2], &pargv[2]);
printf("pargv[3] of %s (%p)\n", pargv[3], &pargv[3]);
if (&pargv[0] == &pargv[1])
printf("addresses of pargv[0] and [1] are the same\n");
通过上述ConvertPyList...
实现,给定 Python list ["foo", "bar", "baz"]
,这些打印语句显示:-
returning address (pargv[0]) of baz (0x7fffffffa410)
pargv[1] of bar (0x7fffffffa418)
pargv[2] of baz (0x7fffffffa420)
pargv[3] of (null) (0x7fffffffa428)
它显然应该显示foo
, bar
, baz
, 而不是显示baz
, bar
, baz
。
让它工作-malloc
并复制字符串
我能够得到并同意的唯一方法是调用foo
每个存储在:-bar
baz
malloc
char*
pargv
const char* const* pyblast::ConvertPyListToCharArrayPointer(
int argc, const boost::python::list& list, const char** pargv)
{
int i=0;
boost::python::stl_input_iterator<std::string> cur_key(list), end;
char* cur_ptr;
while (cur_key != end)
{
// Allocate memory on heap.
cur_ptr = (char*) malloc( strlen( (*cur_key).c_str() ) );
// Copy string into malloc'd memory
if (cur_ptr)
strcpy(cur_ptr, (*cur_key).c_str());
else
fprintf(stderr, "malloc failure!\n");
// Save pointer.
pargv[i++] = cur_ptr;
++cur_key;
}
pargv[i] = NULL;
// Validation code...
const char* const* m_argv = &pargv[0];
return m_argv;
}
但是现在我有很多重复的字符串,我需要在调用代码中释放自己。有没有更安全的 RAII 方法来创建这个const char* const*
数组?
也许这里可以使用一种标准类型的指针(在 STL 或 Boost 中)。使用boost::make_shared
似乎没有奏效......
单元测试代码
如果您希望自己编译或测试代码,以下内容应该会有所帮助:-
我创建了以下 C++ 测试文件:-
/// File: test_converters.cpp
#include "converter.hpp"
// Easiest just to include the source whilst testing...
#include "converter.cpp"
#include <boost/python/def.hpp>
#include <boost/python/list.hpp>
#include <boost/python/module.hpp>
// Create and return the Python list: ["foo", "bar", "baz"]
boost::python::list make_dummy_list(void)
{
boost::python::list dummy_list = boost::python::list();
dummy_list.append("foo");
dummy_list.append("bar");
dummy_list.append("baz");
return dummy_list;
}
int TestPyListConverter(void)
{
// Create data to be tested.
boost::python::list py_argv = make_dummy_list();
ssize_t argc = boost::python::len(py_argv);
const char* pargv[argc+1]; //< pointer array
const char* const* m_pargv = //< type we're converting to / testing.
pyblast::ConvertPyListToCharArrayPointer(argc, py_argv, pargv);
// Free pargv, if necessary...
/// Tests, from Python perspective:-
ssize_t i = 0; // current index on m_pargv
char* p_argv; // temporary pointer
while (m_pargv[i] != NULL && i <= argc)
{
p_argv = PyString_AsString(PyList_GetItem(argv.ptr(), i) );
if( strcmp(m_pargv[i++], p_argv) != 0)
{
PyErr_SetString(PyExc_Exception,
"Test failed. m_pargv[i] != p_argv.");
boost::python::throw_error_already_set();
}
if (i > 4) // didn't find NULL pointer at end of array.
return 1;
}
return 0;
}
BOOST_PYTHON_MODULE(test_converter)
{
boost::python::def("test_py_list_converter", &TestPyListConverter);
}
注意虽然这在使用时有效malloc
,但在我看来它像内存泄漏代码,因为 pargv 中的指针不是free
'd。理想情况下,调用代码不需要担心...
这可以使用以下命令编译:
$ gcc -O1 -shared -g -pipe -fstack-protector -fPIC -I/usr/include/python2.7 -I./ ./test_converters.cpp -o ./test_converter.so -lboost_python -lpython2.7
并执行:
$ python -c 'import test_converter; test_converter.test_py_list_converter()'