2016-05-06 5 views
4

私はC.はブーストからC文字列を返す:: shared_ptrの<string>

で利用可能なC++のメソッドC++ APIメソッドは、通常boost::shared_ptr<T>オブジェクトを返すをするために機能におけるいくつかのC++のコードをラップしています。 C++での私のエクスポート機能は、次のようになります。

extern "C" const char *Hazelcast_Map_get_int_string(
    Hazelcast_Client_t *hazelcastClient, 
    const char *mapName, 
    int key, 
    char** errptr 
) { 
    IMap<int, string> map = hazelcastClient->client->getMap<int, string>(mapName); 
    boost::shared_ptr<string> value = map.get(key); 

    string *strValue = value.get(); 

    return strValue->c_str(); 
} 

私のCクライアントコードは次のようになります。printfは標準出力に正しい値を出力として

const char *stringValue = NULL; 
stringValue = Hazelcast_Map_get_int_string(client, "int_string_map", 10, &err); 

printf("got value from map %s\n", stringValue); 

それは、これまでに動作します。ただし、コードをvalgrindでチェックすると、無効な読み取りエラーが表示されます。だから私はポインタを渡すと何か間違っていると思うが、私は本当に何が問題なのか理解できない。

strdupの値をboost::shared_ptr<string>にすることができますか、それとも私は何をする必要がありますか?

ここvalgrindエラー:

==20635== Invalid read of size 1 
==20635== at 0x10034C6BF: strlen (in /usr/local/Cellar/valgrind/3.11.0/lib/valgrind/vgpreload_memcheck-amd64-darwin.so) 
==20635== by 0x1006136E7: __vfprintf (in /usr/lib/system/libsystem_c.dylib) 
==20635== by 0x10063C35C: __v2printf (in /usr/lib/system/libsystem_c.dylib) 
==20635== by 0x10061201D: vfprintf_l (in /usr/lib/system/libsystem_c.dylib) 
==20635== by 0x10060FEB7: printf (in /usr/lib/system/libsystem_c.dylib) 
==20635== by 0x100001E6A: main (in ./hazelcastCClientTest) 
==20635== Address 0x100de9c41 is 1 bytes inside a block of size 24 free'd 
==20635== at 0x10034B2F7: free (in /usr/local/Cellar/valgrind/3.11.0/lib/valgrind/vgpreload_memcheck-amd64-darwin.so) 
==20635== by 0x1000068BF: void boost::checked_delete<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) (in ./hazelcastCClientTest) 
==20635== by 0x100006888: boost::detail::sp_counted_impl_p<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::dispose() (in ./hazelcastCClientTest) 
==20635== by 0x1000054AD: boost::detail::sp_counted_base::release() (in ./hazelcastCClientTest) 
==20635== by 0x100005449: boost::detail::shared_count::~shared_count() (in ./hazelcastCClientTest) 
==20635== by 0x100005414: boost::detail::shared_count::~shared_count() (in ./hazelcastCClientTest) 
==20635== by 0x1000053F8: boost::shared_ptr<int>::~shared_ptr() (in ./hazelcastCClientTest) 
==20635== by 0x100004054: boost::shared_ptr<int>::~shared_ptr() (in ./hazelcastCClientTest) 
==20635== by 0x10000347F: Hazelcast_Map_get_int_string (in ./hazelcastCClientTest) 
==20635== by 0x100001E54: main (in ./hazelcastCClientTest) 
==20635== Block was alloc'd at 
==20635== at 0x10034AEBB: malloc (in /usr/local/Cellar/valgrind/3.11.0/lib/valgrind/vgpreload_memcheck-amd64-darwin.so) 
==20635== by 0x10038E7DD: operator new(unsigned long) (in /usr/lib/libc++.1.dylib) 
==20635== by 0x1000B8C80: hazelcast::client::serialization::pimpl::DataInput::readUTF() (DataInput.cpp:147) 
==20635== by 0x1000E367F: std::__1::auto_ptr<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > hazelcast::client::serialization::pimpl::SerializationService::toObject<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(hazelcast::client::serialization::pimpl::Data const&) (SerializationService.cpp:590) 
==20635== by 0x10000659D: std::__1::auto_ptr<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > hazelcast::client::proxy::ProxyImpl::toObject<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(hazelcast::client::serialization::pimpl::Data const&) (in ./hazelcastCClientTest) 
==20635== by 0x100006413: std::__1::auto_ptr<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > hazelcast::client::proxy::ProxyImpl::toObject<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::auto_ptr<hazelcast::client::serialization::pimpl::Data>) (in ./hazelcastCClientTest) 
==20635== by 0x10000484B: hazelcast::client::IMap<int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::get(int const&) (in ./hazelcastCClientTest) 
==20635== by 0x10000338F: Hazelcast_Map_get_int_string (in ./hazelcastCClientTest) 
==20635== by 0x100001E54: main (in ./hazelcastCClientTest) 

EDIT:コメントからの質問がwhat is the desired effect?ました。私の最初の目標は、追加のメモリ割り当てをせずにCの呼び出し元に文字列を返すことでしたが、メモリに別の文字列を割り当てて呼び出し元にNULLを返すのが通常のようです。

EDIT 2:私は1つは、C/C#バインディングを通じてleveldbから文字列を取得できるようになるまで起こるどのくらいのコピーについてAyende https://ayende.com/blog/161281/robs-sprint-the-cost-of-getting-data-from-leveldbから記事を思い出したので、私は回避不要なメモリ割り当てしたい理由は、基本的でした。

私は同じ落とし穴を避けたいと思いましたが、追加のメモリ割り当てを最小限に抑えることしかできないと思います。

+3

どのような効果がありますか?あなたは呼び出し元にメモリを解放しますか?それとも、別のやり方で動かしたいですか?呼び出し側が文字列を 'free'したい場合は、' strdup'が最適な解決法です。ポインタをこの関数の次の呼び出しに有効にしたい場合は、おそらく 'shared_ptr'静的にしてください。呼び出し元にバッファーと最大長を渡したいのでしょうか? –

+0

@DavidSchwartzは良い質問ですが、不要なメモリ使用を避けるために自分でメモリを割り当てる必要はありませんが、使いやすいAPIが必要です。 leveldbがhttps://github.com/emnl/leveldb-c-example/blob/master/leveldb_example.cをどのようにしているかを調べようとしましたが、呼び出し元がメモリを解放する責任を負いません。これは私が達成したかったようです。 – Max

+0

leveldb_getは、mallocで作成され、memcpyで埋められた文字列へのポインタを返します。それはいつか解放されなければならない - 私は発信者がそれをするつもりだと思う。ヘッダーファイルによると、 '/ *見つからなければNULLを返します。 malloc()はそれ以外の場合は配列を返します。 配列の長さをvallenに格納します。 */ にextern char型のleveldb_get( leveldb_tの*デシベル、 のconst leveldb_readoptions_t *オプション、 のconstのchar *キー、size_tのKEYLEN、 size_tの* vallen、 のchar ** errptr);私はそれがこのように行って見てきた ' –

答えて

0

実際に何を達成したいのか(簡単で安全なC-API)を考えた後、私はクライアントに新たに割り当てられた文字列を返すことにしました。 (今のところ)最終的な解決策は、次のようになります。

extern "C" int Hazelcast_Map_get_int_string(
    Hazelcast_Client_t *hazelcastClient, 
    const char *mapName, 
    int key, 
    char **value, 
    char** errptr 
) { 
    assert(hazelcastClient != NULL); 
    assert(hazelcastClient->client != NULL); 

    assert(mapName != NULL); 

    assert(value != NULL); 

    try { 
     auto map = hazelcastClient->client->getMap<int, string>(mapName); 
     boost::shared_ptr<string> sharedPtr = map.get(key); 

     string *ptr = sharedPtr.get(); 

     if (ptr == NULL) { 
      return 0; 
     } 

     *value = strdup(ptr->c_str()); 
     return 0; 
    } catch(const std::runtime_error& re) { 
     saveMessageInErrPtr(errptr, re.what()); 
    } catch(const std::exception& ex) { 
     saveMessageInErrPtr(errptr, ex.what()); 
    } catch(...) { 
     saveUnknownErrorOccurredMessageInErrPtr(errptr); 
    } 

    return 1; 
} 

私はerrptrにエラーメッセージを提供することに加えて、成功/失敗の状態を示すために、関数の戻り値を使用することにしました。

クライアントコードは次のようになります。

char *stringValue = NULL; 

if (Hazelcast_Map_get_int_string(client, "int_string_map", 10, &stringValue, &err) != 0) { 
    printf("ERR Map get failed: %s\n", err); 
    Hazelcast_free(err); err = NULL; 
} else { 
    printf("got value from map %s\n", stringValue); 
    free(stringValue); 
} 

私はこれはまだエラー処理を提供しながら使用するのに十分に簡単だと思います。

関連する問題