2

わかりましたので、シリアルを使用してC++ 11で問題が発生しています( http://uscilab.github.io/cereal/ )。

抽象的な意味で、エッジと頂点を接続する多くの共有ポインターでシリアル化する大きなグラフがあります。エッジ (および頂点) にもアトリビュートが関連付けられています。

現在、これらの属性 (基本クラス) の 1 つがアカウント (子クラス) です。Account もシリアル化可能な Idable から継承します。ここで、私の穀物の使用法を示すいくつかの関連するコードの抜粋を示します。このコンテキストの後で問題を説明します。

属性.hpp/cpp

class Attribute {
...

template<class Archive> void serialize(Archive&)
{   
}   

friend class cereal::access;
...

CEREAL_REGISTER_TYPE(mgraph::Attribute)

Idable.hpp/cpp

class Idable {
...

Id id;

template<class Archive> void serialize(Archive& archive)
{
    archive(cereal::make_nvp("id", id)); 
}

template<class Archive> static void load_and_construct(Archive& ar, cereal::construct<mcommon::Idable>& construct)
{
    mcommon::Id id;
    ar(id);
    construct(id);
}

friend class cereal::access;
...

CEREAL_REGISTER_TYPE(mcommon::Idable)

位置.hpp/cpp

class Position
: public mgraph::Attribute
  , public mcommon::Displayable {

template<class Archive> void serialize(Archive& archive)
{   
    archive(cereal::make_nvp("Attribute",
                             cereal::base_class<mgraph::Attribute>(this)));
}   

friend class cereal::access;
...

CEREAL_REGISTER_TYPE(mfin::Position)

Account.hpp/cpp

class Account
: public mcommon::Idable
  , public Position {
...
Currency balance;

template<class Archive> void serialize(Archive& archive)
{   
    archive(cereal::make_nvp("Idable",
                             cereal::base_class<mcommon::Idable>(this)),
            cereal::make_nvp("Position",
                             cereal::base_class<mfin::Position>(this)),
            cereal::make_nvp("balance", balance));
}

template<class Archive> static void load_and_construct(Archive& ar, cereal::construct<Account>& construct)
{
    mcommon::Id iden;
    Currency::Code code;
    ar(iden, code);
    construct(iden, code);
}

friend class cereal::access;
...

CEREAL_REGISTER_TYPE(mfin::Account)

したがって、mfin::Account がシリアル化されているときに問題が発生します。mfin::Account は std::list> に属します。Idable のシリアル化関数に取り掛かると、オブジェクトは無効です。

segfault で停止する gdb に入ると、いくつかのスタックフレームをこの行に移動します: /usr/include/cereal/types/polymorphic.hpp:341。それは次のとおりです。

(gdb) list
336 
337     auto binding = bindingMap.find(std::type_index(ptrinfo));
338     if(binding == bindingMap.end())
339       UNREGISTERED_POLYMORPHIC_EXCEPTION(save, cereal::util::demangle(ptrinfo.name()))
340 
341     binding->second.shared_ptr(&ar, ptr.get());
342   }
343 
344   //! Loading std::shared_ptr for polymorphic types
345   template <class Archive, class T> inline

ここで、これが ptr です。

(gdb) print *((mfin::Account*)(ptr.get()))
$10 = {<mcommon::Idable> = {_vptr.Idable = 0x4f0d50 <vtable for mfin::Account+16>, id = "bank"}, <mfin::Position> = {<mgraph::Attribute> = {
      _vptr.Attribute = 0x4f0d78 <vtable for mfin::Account+56>}, <mcommon::Displayable> = {_vptr.Displayable = 0x4f0da0 <vtable for mfin::Account+96>}, <No data fields>}, balance = {<mcommon::Displayable> = {
      _vptr.Displayable = 0x4f0570 <vtable for mfin::Currency+16>}, amount = 0, code = mfin::Currency::USD}}
(gdb) print ptr
$11 = std::shared_ptr (count 3, weak 0) 0x758ad0

すべてが良さそうです。ただし、void* にキャストするときに注意してください。

$11 = std::shared_ptr (count 3, weak 0) 0x758ad0
(gdb) print *((mfin::Account*)((void*)ptr.get()))
$12 = {<mcommon::Idable> = {_vptr.Idable = 0x4f0d78 <vtable for mfin::Account+56>, 
    id = "\363aL\000\000\000\000\000PbL\000\000\000\000\000\304\031L\000\000\000\000\000\021#L", '\000' <repeats 13 times>, " \232N", '\000' <repeats 21 times>, "P\251@\000\000\000\000\000\370\377\377\377\377\377\377\377 \232N", '\000' <repeats 21 times>, "\304\031L\000\000\000\000\000P\251@", '\000' <repeats 45 times>, "St19_Sp_counted_deleterIPN4mfin7AccountE"...}, <mfin::Position> = {<mgraph::Attribute> = {
      _vptr.Attribute = 0x4f0570 <vtable for mfin::Currency+16>}, <mcommon::Displayable> = {_vptr.Displayable = 0x0}, <No data fields>}, balance = {<mcommon::Displayable> = {_vptr.Displayable = 0x0}, amount = 49, 
    code = (unknown: 7702648)}}

これはもちろん、const void* を取る binding->second.shared_ptr (以下を参照) で起こることです。

(gdb) list
295             writeMetadata(ar);
296 
297             #ifdef _MSC_VER
298             savePolymorphicSharedPtr( ar, dptr, ::cereal::traits::has_shared_from_this<T>::type() ); // MSVC doesn't like typename here
299             #else // not _MSC_VER
300             savePolymorphicSharedPtr( ar, dptr, typename ::cereal::traits::has_shared_from_this<T>::type() );
301             #endif // _MSC_VER
302           };
303 
304         serializers.unique_ptr =

これを引き起こす穀物の使用法で何が間違っていますか? これが私が得る最終的なエラーです:

Program received signal SIGSEGV, Segmentation fault.
0x000000000040f7cd in rapidjson::Writer<rapidjson::GenericWriteStream, rapidjson::UTF8<char>, rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> >::WriteString (this=0x7fffffffd358, 
    str=0x4f1ae0 <vtable for mfin::Account+96> "\363aL", length=4989722) at /usr/include/cereal/external/rapidjson/writer.h:276
276             if ((sizeof(Ch) == 1 || characterOk(*p)) && escape[(unsigned char)*p])  {
Missing separate debuginfos, use: debuginfo-install boost-date-time-1.55.0-8.fc21.x86_64 boost-filesystem-1.55.0-8.fc21.x86_64 boost-program-options-1.55.0-8.fc21.x86_64 boost-system-1.55.0-8.fc21.x86_64 boost-thread-1.55.0-8.fc21.x86_64 fcgi-2.4.0-24.fc21.x86_64 glog-0.3.3-3.128tech.x86_64 libgcc-4.9.2-1.fc21.x86_64 libstdc++-4.9.2-1.fc21.x86_64
4

3 に答える 3

3

多くの調査の結果、問題に対する答えがあると思います。そして、これはライブラリのバグだと思います。これを図書館の所有者に確認した後、そこにある結果でこれが最新であることを確認します.

この問題を示す簡単なプログラムを以下に作成しました。この問題は、多重継承、ポリモーフィズム、およびキャストに起因します。以下のプログラムでは、派生オブジェクトを作成していることに注意してください。Derived オブジェクトをメモリに配置すると、次のような形式になります。

Derived:
  Base2::vtable
  Base2::var
  Base::vtable

検討:

(gdb) print ptr
$2 = std::shared_ptr (count 1, weak 0) 0x63c580
(gdb) print *ptr
$3 = (Derived &) @0x63c580: {<Base2> = {_vptr.Base2 = 0x421f90 <vtable for Derived+16>, var = ""}, <Base> = {_vptr.Base = 0x421fa8 <vtable for Derived+40>}, <No data fields>}

これを base に dynamic_pointer_cast すると、次のようになります。

(gdb) print ptr
$8 = std::shared_ptr (count 2, weak 0) 0x63c590
(gdb) print *ptr
$9 = (Base &) @0x63c590: {_vptr.Base = 0x421fa8 <vtable for Derived+40>}

ここから問題が始まります。/usr/include/cereal/types/polymorphic.hpp の 341 行目です。Base へのこの ptr があります。ここに次のものがあります。

binding->second.shared_ptr(&ar, ptr.get());

これは const void* へのキャストになります。後で型情報に基づいて、登録されたポリモーフィック型から型をキャストします。shared_ptr は Derived 型のオブジェクトを指すため、これは Derived* を意味します。以下に示すように:

272       static inline void savePolymorphicSharedPtr( Archive & ar, void const * dptr, std::false_type /* has_shared_from_this */ )
273       {
274         PolymorphicSharedPointerWrapper psptr( dptr );
275         ar( CEREAL_NVP_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) );
276       }

これは、Base* であるスタック ptr が void* にキャストされ、次に Derived* にキャストされたことを意味します。したがって、キャストチェーンは無効なオブジェクトになります。以下に示すように、ptr は現在無効です。

(gdb) print *ptr
$7 = (const Derived &) @0x63c590: {<Base2> = {_vptr.Base2 = 0x421fa8 <vtable for Derived+40>, var = <error reading variable: Cannot access memory at address 0x49>}, <Base> = {_vptr.Base = 0x0}, <No data fields>}

ポインターは、本来あるべき Derived/Base2 ではなく、Base の vtable を指しているため、プログラムがクラッシュします。

{
    "ptr": {
        "polymorphic_id": 2147483649,
        "polymorphic_name": "Derived",
        "ptr_wrapper": {
            "id": 2147483649,
            "data": {
                "Base2": {

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b8e9e3 in std::string::size() const () from /lib64/libstdc++.so.6

以下は、これを再現するサンプルプログラムです。

// g++ test.cpp -std=c++11 -ggdb -o test && gdb ./test
#include <cereal/archives/json.hpp>
#include <cereal/types/polymorphic.hpp>
#include <iostream>

struct Base {
    virtual void foo() { } 
    template<class Archive> void serialize(Archive& archive) { } 
};

struct Base2 {
    virtual void foo() { } 
    std::string var;
    template<class Archive> void serialize(Archive& archive) {   
        archive(cereal::make_nvp("var", var));
    }   
};

struct Derived : public Base2, public Base {
    template<class Archive> void serialize(Archive& archive) {   
        archive(cereal::make_nvp("Base2",
                                 cereal::base_class<Base2>(this)),
                cereal::make_nvp("Base",
                                 cereal::base_class<Base>(this)));
    }   
};

CEREAL_REGISTER_TYPE(Base);
CEREAL_REGISTER_TYPE(Base2);
CEREAL_REGISTER_TYPE(Derived);

int main() {
    auto ptr = std::make_shared<Derived>();
    cereal::JSONOutputArchive ar(std::cout);
    ar(cereal::make_nvp("ptr", std::dynamic_pointer_cast<Base>(ptr)));

    return 0;
}
于 2015-04-29T07:10:09.003 に答える