引数の型としてa を受け入れるサードパーティのライブラリ関数を呼び出したいconst char* const*
:-
class Arguments
{
public:
Arguments(int argc, const char* const* p_argv = 0);
private:
deque<std::string> m_Args;
}
ライブラリの実装では、渡されるオブジェクトは通常、 の最初の要素への (const) ポインタですchar** argv
。次に、文字列はプライベート メンバー変数 ( m_Args
) にキャッシュされるため、ポインターは構築中にのみ有効である必要があると思います。p_argv
主な質問:const char* const* p_argv
その変数を作成するにはどうすればよいですか、または作成する必要がありますか?
詳しい説明
list
他の配列型 (Boost Pythonおよびs)から変換されたオブジェクトを渡そうとしていますが、安全な ( RAII ) 方法をtuple
見つけようとしています。
コード内のさまざまな場所でこれを行うため、型変換を実行できる再利用可能な関数を実装しようとしました。例えば
// 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;
}
何が起こっているのかを正確に確認するために、大量の print ステートメントを展開しました。それが言うところで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"]
と、これらの print ステートメントは次のように表示されます。
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;
}
しかし今、呼び出し元のコードで自分自身を解放する必要がある重複した文字列がたくさんあります。const char* const*
このアレイを作成するためのより安全な RAII のような方法はありますか?
おそらく、ここで使用できる標準タイプのポインター (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);
}
NB を使用するとこれは機能しますが、pargv のポインタが'd'malloc
されていないため、メモリ リーク コードのように見えます。free
理想的には、呼び出しコードはそれについて心配する必要はありません...
これは、次のコマンドでコンパイルできます。
$ 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()'