从boost :: shared_ptr返回C字符串<string>

时间:2016-05-06 00:30:28

标签: c++ c

我在函数中包含了一些C ++代码,以便在C语言中使用C ++方法。

C ++ API方法通常会返回boost::shared_ptr<T>个对象。我在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客户端代码如下所示:

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

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

它到目前为止工作,因为printf在stdout上输出正确的值。但是,使用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)

编辑:评论中的问题是what is the desired effect?。我最初的目标是将字符串返回给C调用者而不需要任何额外的内存分配,但似乎通常的方法是在内存中分配另一个字符串并将其返回给调用者或NULL

编辑2:我想避免不必要的内存分配的原因基本上是因为我记得来自Ayende https://ayende.com/blog/161281/robs-sprint-the-cost-of-getting-data-from-leveldb的一篇文章,关于在从leveldb通过leveldb获取字符串之前发生了多少复制C / C#绑定。

我想避免同样的陷阱,但我认为只能最大限度地减少额外的内存分配。

1 个答案:

答案 0 :(得分: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);
}

我认为这很容易使用,同时仍然提供错误处理。