2

引数の型として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呼び出す関数から、スタック上に作成できることを望みました。ただし、コードの単体テストでは、保存された文字列がクラスから復元されると無効になることが示されています。(これは理にかなっています。ポインターの配列にストレージを割り当てますが、それらのポインターが指す文字列には割り当てません)。ConvertPyListToCharArrayPointerstore_argumentsArguments


コンバーターの実装

元の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代わりに,を表示します。bazbarbaz


動作させる-mallocそして文字列をコピーする

foo私が取得しbarbaz同意することができた唯一の方法は、に保存され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()'
4

1 に答える 1

1

あなたの主な質問が、キューを const char* const * に変換する方法である場合、何かへのポインターは、何かの配列の最初の要素を指すために時々使用されます。そのため、 const char* const * の場合、char への配列へのポインターを持つ配列の最初の要素へのポインターがおそらく意味されます。配列ごとに、配列の長さを示す何らかのプロトコルが必要です。const char* の場合、これは通常 0 で終了する C 文字列である可能性があります。char へのポインターへのポインターの配列の場合、配列の終わりを示すために 0 を使用することもできます。質問に戻る:

std::vector を使用して、キューのすべての要素に対して std::string.c_str() によって返される値で初期化できます。ベクトルに最後の 0 ポインターを追加します。ベクトルの最初の要素のアドレスを const char* const * に変換できるようになりました。

取得したポインターは、ポインターのベクトルと文字列のキューが有効である限り有効です。

std::deque< std::string > input;
std::vector< const char* > v;

for ( std::deque< std::string >::const_iterator i = input.begin(); i != input.end(); ++i )
    v.push_back( i->c_str() );

v.push_back( 0 );

const char* const * p = &v[ 0 ];
于 2013-04-11T11:00:46.660 に答える