c++ 如何使用Cereal序列化igraph图而不会遇到AddressSanitizer问题?

idfiyjo8  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(98)

我有一个使用igraph库生成的图。现在,我想序列化这个图。
虽然igraph提供了一些方法来将图形转换为文件,但它们都缺乏对属性的支持,或者需要图形的某些特定问题背景,这些都没有在我的案例中给出。此外,我实际上试图解决的问题中的这个图形是一个更大的类的成员,我使用Cereal库序列化了这个类。
在下面的实现中,AddressSanitizer报告了一个allocation-size-too-big问题,这意味着我的实现不正确,或者我误解了Cereal的内部机制。
我试图有一个一致的实现看起来像这样:

namespace cereal {
////////////////////////////////////////////////////////////////
// serialization of igraph objects
template <class Archive>
inline void CEREAL_SAVE_FUNCTION_NAME(Archive &ar, igraph_t const &graph) {
  size_t numVertices = igraph_vcount(&graph);
  size_t numEdges = igraph_ecount(&graph);

  igraph_vector_int_t allEdges;
  igraph_vector_int_init(&allEdges, numEdges);
  if (igraph_edges(&graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &allEdges)) {
    throw std::runtime_error("Failed to get all edges");
  }
  std::vector<long int> edges;
  utils::igraphVectorTToStdVector(&allEdges, edges);
  igraph_vector_int_destroy(&allEdges);

  ar(numVertices);
  ar(numEdges);
  ar(edges);

  // after storing the edges, must also store the attributes
  // query them first
  igraph_strvector_t gnames;
  igraph_strvector_init(&gnames, 1);
  igraph_vector_int_t gtypes;
  igraph_vector_int_init(&gtypes, 1);
  igraph_strvector_t vnames;
  igraph_strvector_init(&vnames, 1);
  igraph_vector_int_t vtypes;
  igraph_vector_int_init(&vtypes, 1);
  igraph_strvector_t enames;
  igraph_strvector_init(&enames, 1);
  igraph_vector_int_t etypes;
  igraph_vector_int_init(&etypes, 1);
  igraph_cattribute_list(&graph, &gnames, &gtypes, &vnames, &vtypes, &enames,
                         &etypes);

  if (igraph_strvector_size(&gnames) != 0) {
    throw std::runtime_error(
        "Graph attributes serialization not supported yet.");
  }

  // serizalize vertex attributes
  size_t numVertexAttributes = igraph_strvector_size(&vnames);
  ar(make_size_tag(numVertexAttributes));
  for (size_t i = 0; i < numVertexAttributes; i++) {
    const char *name = igraph_strvector_get(&vnames, i);
    ar(std::string(name));
    ar(igraph_vector_int_get(&vtypes, i));
    switch (igraph_vector_int_get(&vtypes, i)) {
    // case IGRAPH_ATTRIBUTE_DEFAULT:
    case IGRAPH_ATTRIBUTE_NUMERIC: {
      igraph_vector_t results;
      igraph_vector_init(&results, numVertices);
      igraph_cattribute_VANV(&graph, igraph_strvector_get(&vnames, i),
                             igraph_vss_all(), &results);
      std::vector<double> attributes;
      utils::igraphVectorTToStdVector(&results, attributes);
      ar(attributes);
      igraph_vector_destroy(&results);
    } break;
    case IGRAPH_ATTRIBUTE_STRING: {
      igraph_strvector_t strresults;
      igraph_strvector_init(&strresults, numVertices);
      igraph_cattribute_VASV(&graph, igraph_strvector_get(&vnames, i),
                             igraph_vss_all(), &strresults);
      std::vector<std::string> strattributes;
      utils::igraphVectorTToStdVector(&strresults, strattributes);
      ar(strattributes);
      igraph_strvector_destroy(&strresults);
    } break;
    default:
      throw std::runtime_error(
          "This attribute type (" +
          std::to_string(igraph_vector_int_get(&vtypes, i)) +
          ") is not supported");
    }
  }

  // serizalize edge attributes
  size_t numEdgeAttributes = igraph_strvector_size(&enames);
  ar(make_size_tag(numEdgeAttributes));
  for (size_t i = 0; i < numEdgeAttributes; i++) {
    const char *name = igraph_strvector_get(&enames, i);
    ar(std::string(name));
    ar(igraph_vector_int_get(&etypes, i));
    switch (igraph_vector_int_get(&etypes, i)) {
    // case IGRAPH_ATTRIBUTE_DEFAULT:
    case IGRAPH_ATTRIBUTE_NUMERIC: {
      igraph_vector_t results;
      igraph_vector_init(&results, numEdges);
      igraph_cattribute_EANV(&graph, igraph_strvector_get(&enames, i),
                             igraph_ess_all(IGRAPH_EDGEORDER_ID), &results);
      std::vector<double> attributes;
      utils::igraphVectorTToStdVector(&results, attributes);
      ar(attributes);
      igraph_vector_destroy(&results);
    } break;
    case IGRAPH_ATTRIBUTE_STRING: {
      igraph_strvector_t strresults;
      igraph_strvector_init(&strresults, numEdges);
      igraph_cattribute_EASV(&graph, igraph_strvector_get(&enames, i),
                             igraph_ess_all(IGRAPH_EDGEORDER_ID), &strresults);
      std::vector<std::string> strattributes;
      utils::igraphVectorTToStdVector(&strresults, strattributes);
      ar(strattributes);
      igraph_strvector_destroy(&strresults);
    } break;
    default:
      throw std::runtime_error(
          "This attribute type (" +
          std::to_string(igraph_vector_int_get(&etypes, i)) +
          ") is not supported");
    }
  }

  igraph_strvector_destroy(&gnames);
  igraph_strvector_destroy(&enames);
  igraph_strvector_destroy(&vnames);

  igraph_vector_int_destroy(&gtypes);
  igraph_vector_int_destroy(&etypes);
  igraph_vector_int_destroy(&vtypes);
}

template <class Archive>
inline void CEREAL_LOAD_FUNCTION_NAME(Archive &ar, igraph_t &graph) {
  size_t numVertices;
  size_t numEdges;
  ar(numVertices);
  ar(numEdges);
  std::vector<long int> edges;
  ar(edges);
  igraph_vector_int_t allEdges;
  igraph_vector_int_init(&allEdges, numEdges);
  utils::StdVectorToIgraphVectorT(edges, &allEdges);

  igraph_add_vertices(&graph, numVertices, nullptr);
  igraph_add_edges(&graph, &allEdges, nullptr);

  // deserialize vertex attributes
  size_type numVertexAttributes;
  ar(make_size_tag(numVertexAttributes));

  for (size_t i = 0; i < numVertexAttributes; ++i) {
    std::string attributeName;
    ar(attributeName);
    int attributeType;
    ar(attributeType);
    switch (attributeType) {
    // case IGRAPH_ATTRIBUTE_DEFAULT:
    case IGRAPH_ATTRIBUTE_NUMERIC: {
      std::vector<double> attributes;
      ar(attributes);
      igraph_vector_t results;
      igraph_vector_init(&results, attributes.size());
      utils::StdVectorToIgraphVectorT(attributes, &results);
      igraph_cattribute_VAN_setv(&graph, attributeName.c_str(), &results);
      igraph_vector_destroy(&results);
    }; break;
    case IGRAPH_ATTRIBUTE_STRING: {
      std::vector<std::string> strattributes;
      ar(strattributes);
      igraph_strvector_t strresults;
      igraph_strvector_init(&strresults, strattributes.size());
      utils::StdVectorToIgraphVectorT(strattributes, &strresults);
      igraph_cattribute_VAS_setv(&graph, attributeName.c_str(), &strresults);
      igraph_strvector_destroy(&strresults);
    }; break;
    default:
      throw std::runtime_error("This attribute type (" +
                               std::to_string(attributeType) +
                               ") is not supported");
    }
  }

  // and same for edge attributes
  size_type numEdgeAttributes;
  ar(make_size_tag(numEdgeAttributes));
  for (size_t i = 0; i < numEdgeAttributes; ++i) {
    std::string attributeName;
    ar(attributeName);
    int attributeType;
    ar(attributeType);
    switch (attributeType) {
    // case IGRAPH_ATTRIBUTE_DEFAULT:
    case IGRAPH_ATTRIBUTE_NUMERIC: {
      std::vector<double> attributes;
      ar(attributes);
      igraph_vector_t results;
      igraph_vector_init(&results, 1);
      utils::StdVectorToIgraphVectorT(attributes, &results);
      igraph_cattribute_EAN_setv(&graph, attributeName.c_str(), &results);
      igraph_vector_destroy(&results);
    } break;
    case IGRAPH_ATTRIBUTE_STRING: {
      std::vector<std::string> strattributes;
      ar(strattributes);
      igraph_strvector_t strresults;
      igraph_strvector_init(&strresults, 1);
      utils::StdVectorToIgraphVectorT(strattributes, &strresults);
      igraph_cattribute_EAS_setv(&graph, attributeName.c_str(), &strresults);
      igraph_strvector_destroy(&strresults);
    } break;
    default:
      throw std::runtime_error("This attribute type (" +
                               std::to_string(attributeType) +
                               ") is not supported");
    }
  }
}
} // namespace cereal

字符串
可以在这里找到启动和运行的实现:https://github.com/GenieTim/igraph-cereal-serialisation
这个实现的问题是,如上所述,AddressSanitizer在重命名时报告allocation-size-too-big,据说是从Cereal内部分配的,用于重命名属性std::vector。是我的实现不正确,还是Cereal应该分配这么多内存,我应该简单地禁用AddressSanitizer?
下面是我得到的堆栈跟踪:

=================================================================
==86488==ERROR: AddressSanitizer: requested allocation size 0x4e2000000000 (0x4e2000001000 after adjustments for alignment, red zones etc.) exceeds maximum supported size of 0x10000000000 (thread T0)
    #0 0x10f7f5fcd in _Znwm+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0xf0fcd)
    #1 0x10ea32684 in void* std::__1::__libcpp_operator_new[abi:ue170004]<unsigned long>(unsigned long) new:268
    #2 0x10ea3262c in std::__1::__libcpp_allocate[abi:ue170004](unsigned long, unsigned long) new:294
    #3 0x10ea35287 in std::__1::allocator<double>::allocate[abi:ue170004](unsigned long) allocator.h:114
    #4 0x10ea3517c in std::__1::__allocation_result<std::__1::allocator_traits<std::__1::allocator<double>>::pointer> std::__1::__allocate_at_least[abi:ue170004]<std::__1::allocator<double>>(std::__1::allocator<double>&, unsigned long) allocate_at_least.h:55
    #5 0x10ea350c8 in std::__1::__split_buffer<double, std::__1::allocator<double>&>::__split_buffer(unsigned long, unsigned long, std::__1::allocator<double>&) __split_buffer:379
    #6 0x10ea34e9c in std::__1::__split_buffer<double, std::__1::allocator<double>&>::__split_buffer(unsigned long, unsigned long, std::__1::allocator<double>&) __split_buffer:375
    #7 0x10ea3a8f8 in std::__1::vector<double, std::__1::allocator<double>>::__append(unsigned long) vector:1162
    #8 0x10ea3a79f in std::__1::vector<double, std::__1::allocator<double>>::resize(unsigned long) vector:1981
    #9 0x10ea3a71e in std::__1::enable_if<traits::is_input_serializable<cereal::BinaryData<double>, cereal::BinaryInputArchive>::value && std::is_arithmetic<double>::value && !std::is_same<double, bool>::value, void>::type cereal::load<cereal::BinaryInputArchive, double, std::__1::allocator<double>>(cereal::BinaryInputArchive&, std::__1::vector<double, std::__1::allocator<double>>&) vector.hpp:57
    #10 0x10ea3a6b4 in cereal::BinaryInputArchive& cereal::InputArchive<cereal::BinaryInputArchive, 1u>::processImpl<std::__1::vector<double, std::__1::allocator<double>>, (cereal::traits::detail::sfinae)0>(std::__1::vector<double, std::__1::allocator<double>>&) cereal.hpp:941
    #11 0x10ea3a665 in void cereal::InputArchive<cereal::BinaryInputArchive, 1u>::process<std::__1::vector<double, std::__1::allocator<double>>&>(std::__1::vector<double, std::__1::allocator<double>>&) cereal.hpp:853
    #12 0x10ea39410 in cereal::BinaryInputArchive& cereal::InputArchive<cereal::BinaryInputArchive, 1u>::operator()<std::__1::vector<double, std::__1::allocator<double>>&>(std::__1::vector<double, std::__1::allocator<double>>&) cereal.hpp:730
    #13 0x10ea38b37 in void cereal::load<cereal::BinaryInputArchive>(cereal::BinaryInputArchive&, igraph_s&) main.cpp:228
    #14 0x10ea38964 in cereal::BinaryInputArchive& cereal::InputArchive<cereal::BinaryInputArchive, 1u>::processImpl<igraph_s, (cereal::traits::detail::sfinae)0>(igraph_s&) cereal.hpp:941
    #15 0x10ea38915 in void cereal::InputArchive<cereal::BinaryInputArchive, 1u>::process<igraph_s&>(igraph_s&) cereal.hpp:853
    #16 0x10ea25250 in cereal::BinaryInputArchive& cereal::InputArchive<cereal::BinaryInputArchive, 1u>::operator()<igraph_s&>(igraph_s&) cereal.hpp:730
    #17 0x10ea24baa in main main.cpp:333
    #18 0x7ff8141fe3a5 in start+0x795 (dyld:x86_64+0xfffffffffff5c3a5)

==86488==HINT: if you don't care about these errors you may set allocator_may_return_null=1
SUMMARY: AddressSanitizer: allocation-size-too-big (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0xf0fcd) in _Znwm+0x7d
==86488==ABORTING
./bin/compileAndRun.sh: line 10: 86488 Abort trap: 6           MallocNanoZone=0 ASAN_OPTIONS=detect_leaks=1 ./main

nwlqm0z1

nwlqm0z11#

事实证明,Cereal似乎不允许我所期望的这种层次结构形式。相反,make_size_tagsome 类型的数组(向量)的应用有限。
实际上,属性不能像我尝试的那样在内部循环中序列化;一个可能的工作实现可能看起来像这样(igraph向量也在自己的方法中序列化,而不是先将它们转换为std::vector):

namespace cereal {
////////////////////////////////////////////////////////////////
// serialization of igraph objects
// igraph vectors
template <class Archive>
inline void CEREAL_SAVE_FUNCTION_NAME(Archive &ar,
                                      igraph_vector_int_t const &vec) {
  size_type n = igraph_vector_int_size(&vec);
  ar(make_size_tag(n));
  for (size_type i = 0; i < n; ++i) {
    ar(igraph_vector_int_get(&vec, i));
  }
}

template <class Archive>
inline void CEREAL_LOAD_FUNCTION_NAME(Archive &ar, igraph_vector_int_t &vec) {
  size_type n;
  ar(make_size_tag(n));
  igraph_vector_int_resize(&vec, n);
  for (size_type i = 0; i < n; ++i) {
    long int val;
    ar(val);
    igraph_vector_int_set(&vec, i, val);
  }
}

template <class Archive>
inline void CEREAL_SAVE_FUNCTION_NAME(Archive &ar, igraph_vector_t const &vec) {
  size_type n = igraph_vector_size(&vec);
  ar(make_size_tag(n));
  for (size_type i = 0; i < n; ++i) {
    ar(igraph_vector_get(&vec, i));
  }
}

template <class Archive>
inline void CEREAL_LOAD_FUNCTION_NAME(Archive &ar, igraph_vector_t &vec) {
  size_type n;
  ar(make_size_tag(n));
  igraph_vector_resize(&vec, n);
  for (size_type i = 0; i < n; ++i) {
    double val;
    ar(val);
    igraph_vector_set(&vec, i, val);
  }
}

template <class Archive>
inline void CEREAL_SAVE_FUNCTION_NAME(Archive &ar,
                                      igraph_strvector_t const &vec) {
  size_type n = igraph_strvector_size(&vec);
  ar(make_size_tag(n));
  for (size_type i = 0; i < n; ++i) {
    std::string val = igraph_strvector_get(&vec, i);
    ar(val);
  }
}

template <class Archive>
inline void CEREAL_LOAD_FUNCTION_NAME(Archive &ar, igraph_strvector_t &vec) {
  size_type n;
  ar(make_size_tag(n));
  igraph_strvector_resize(&vec, n);
  std::string val;
  val.reserve(50);
  for (size_type i = 0; i < n; ++i) {
    val.clear();
    ar(val);
    igraph_strvector_set(&vec, i, val.c_str());
  }
}

// the graph
template <class Archive>
inline void CEREAL_SAVE_FUNCTION_NAME(Archive &ar, igraph_t const &graph) {
  size_t numVertices = igraph_vcount(&graph);
  ar(make_nvp("num_vertices", numVertices));
  size_t numEdges = igraph_ecount(&graph);
  ar(make_nvp("num_edges", numEdges));

  igraph_vector_int_t allEdges;
  igraph_vector_int_init(&allEdges, numEdges);
  if (igraph_edges(&graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &allEdges)) {
    throw std::runtime_error("Failed to get all edges");
  }

  ar(make_nvp("edges", allEdges));
  igraph_vector_int_destroy(&allEdges);

  // after storing the edges, must also store the attributes
  // query them first
  igraph_strvector_t gnames;
  igraph_strvector_init(&gnames, 1);
  igraph_vector_int_t gtypes;
  igraph_vector_int_init(&gtypes, 1);
  igraph_strvector_t vnames;
  igraph_strvector_init(&vnames, 1);
  igraph_vector_int_t vtypes;
  igraph_vector_int_init(&vtypes, 1);
  igraph_strvector_t enames;
  igraph_strvector_init(&enames, 1);
  igraph_vector_int_t etypes;
  igraph_vector_int_init(&etypes, 1);
  igraph_cattribute_list(&graph, &gnames, &gtypes, &vnames, &vtypes, &enames,
                         &etypes);

  if (igraph_strvector_size(&gnames) != 0) {
    throw std::runtime_error(
        "Graph attributes serialization not supported yet.");
  }

  // serizalize vertex attributes
  size_type numVertexAttributes = igraph_strvector_size(&vnames);
  assert(igraph_strvector_size(&vnames) == igraph_vector_int_size(&vtypes));
  ar(make_nvp("vertex_attr_names", vnames));
  ar(make_nvp("vertex_attr_types", vtypes));
  //   ar(make_size_tag(numVertexAttributes));
  for (size_t i = 0; i < numVertexAttributes; i++) {
    const char *name = igraph_strvector_get(&vnames, i);
    std::string namestr = std::string(name);
    switch (igraph_vector_int_get(&vtypes, i)) {
    // case IGRAPH_ATTRIBUTE_DEFAULT:
    case IGRAPH_ATTRIBUTE_NUMERIC: {
      igraph_vector_t results;
      igraph_vector_init(&results, numVertices);
      igraph_cattribute_VANV(&graph, igraph_strvector_get(&vnames, i),
                             igraph_vss_all(), &results);
      ar(make_nvp("vertex_attr_" + namestr, results));
      igraph_vector_destroy(&results);
    } break;
    case IGRAPH_ATTRIBUTE_STRING: {
      igraph_strvector_t strresults;
      igraph_strvector_init(&strresults, numVertices);
      igraph_cattribute_VASV(&graph, igraph_strvector_get(&vnames, i),
                             igraph_vss_all(), &strresults);
      ar(make_nvp("vertex_attr_" + namestr, strresults));
      igraph_strvector_destroy(&strresults);
    } break;
    default:
      throw std::runtime_error(
          "This attribute type (" +
          std::to_string(igraph_vector_int_get(&vtypes, i)) +
          ") is not supported");
    }
  }

  // serizalize edge attributes
  size_type numEdgeAttributes = igraph_strvector_size(&enames);
  assert(igraph_strvector_size(&enames) == igraph_vector_int_size(&etypes));
  ar(make_nvp("edge_attr_names", enames));
  ar(make_nvp("edge_attr_types", etypes));
  //   ar(make_size_tag(numEdgeAttributes * 3));
  for (size_t i = 0; i < numEdgeAttributes; i++) {
    const char *name = igraph_strvector_get(&enames, i);
    std::string namestr = std::string(name);
    switch (igraph_vector_int_get(&etypes, i)) {
    // case IGRAPH_ATTRIBUTE_DEFAULT:
    case IGRAPH_ATTRIBUTE_NUMERIC: {
      igraph_vector_t results;
      igraph_vector_init(&results, numEdges);
      igraph_cattribute_EANV(&graph, igraph_strvector_get(&enames, i),
                             igraph_ess_all(IGRAPH_EDGEORDER_ID), &results);
      ar(make_nvp("edge_attr_" + namestr, results));
      igraph_vector_destroy(&results);
    } break;
    case IGRAPH_ATTRIBUTE_STRING: {
      igraph_strvector_t strresults;
      igraph_strvector_init(&strresults, numEdges);
      igraph_cattribute_EASV(&graph, igraph_strvector_get(&enames, i),
                             igraph_ess_all(IGRAPH_EDGEORDER_ID), &strresults);
      ar(make_nvp("edge_attr_" + namestr, strresults));
      igraph_strvector_destroy(&strresults);
    } break;
    default:
      throw std::runtime_error(
          "This attribute type (" +
          std::to_string(igraph_vector_int_get(&etypes, i)) +
          ") is not supported");
    }
  }

  igraph_strvector_destroy(&gnames);
  igraph_strvector_destroy(&enames);
  igraph_strvector_destroy(&vnames);

  igraph_vector_int_destroy(&gtypes);
  igraph_vector_int_destroy(&etypes);
  igraph_vector_int_destroy(&vtypes);
}

template <class Archive>
inline void CEREAL_LOAD_FUNCTION_NAME(Archive &ar, igraph_t &graph) {
  size_t numVertices;
  ar(make_nvp("num_vertices", numVertices));
  size_t numEdges;
  ar(make_nvp("num_edges", numEdges));
  igraph_vector_int_t allEdges;
  igraph_vector_int_init(&allEdges, numEdges);
  ar(make_nvp("edges", allEdges));

  igraph_add_vertices(&graph, numVertices, nullptr);
  igraph_add_edges(&graph, &allEdges, nullptr);
  igraph_vector_int_destroy(&allEdges);

  // deserialize vertex attributes
  igraph_strvector_t vnames;
  igraph_strvector_init(&vnames, 1);
  ar(make_nvp("vertex_attr_names", vnames));
  igraph_vector_int_t vtypes;
  igraph_vector_int_init(&vtypes, 1);
  ar(make_nvp("vertex_attr_types", vtypes));

  size_type numVertexAttributes = igraph_vector_int_size(&vtypes);
  for (size_t i = 0; i < numVertexAttributes; ++i) {
    std::string attributeName = std::string(igraph_strvector_get(&vnames, i));
    int attributeType = igraph_vector_int_get(&vtypes, i);
    switch (attributeType) {
    // case IGRAPH_ATTRIBUTE_DEFAULT:
    case IGRAPH_ATTRIBUTE_NUMERIC: {
      igraph_vector_t results;
      igraph_vector_init(&results, numVertices);
      ar(make_nvp("vertex_attr_" + attributeName, results));
      igraph_cattribute_VAN_setv(&graph, attributeName.c_str(), &results);
      igraph_vector_destroy(&results);
    }; break;
    case IGRAPH_ATTRIBUTE_STRING: {
      igraph_strvector_t strresults;
      igraph_strvector_init(&strresults, numVertices);
      ar(make_nvp("vertex_attr_" + attributeName, strresults));
      igraph_cattribute_VAS_setv(&graph, attributeName.c_str(), &strresults);
      igraph_strvector_destroy(&strresults);
    }; break;
    default:
      throw std::runtime_error("This attribute type (" +
                               std::to_string(attributeType) +
                               ") is not supported");
    }
  }
  igraph_vector_int_destroy(&vtypes);
  igraph_strvector_destroy(&vnames);

  // and same for edge attributes
  igraph_strvector_t enames;
  igraph_strvector_init(&enames, 1);
  ar(make_nvp("edge_attr_names", enames));
  igraph_vector_int_t etypes;
  igraph_vector_int_init(&etypes, 1);
  ar(make_nvp("edge_attr_types", etypes));

  size_t numEdgeAttributes = igraph_vector_int_size(&etypes);
  for (size_t i = 0; i < numEdgeAttributes; ++i) {
    std::string attributeName = std::string(igraph_strvector_get(&enames, i));
    
    int attributeType = igraph_vector_int_get(&etypes, i);
    switch (attributeType) {
    // case IGRAPH_ATTRIBUTE_DEFAULT:
    case IGRAPH_ATTRIBUTE_NUMERIC: {
      igraph_vector_t results;
      igraph_vector_init(&results, 1);
      ar(make_nvp("edge_attr_" + attributeName, results));
      igraph_cattribute_EAN_setv(&graph, attributeName.c_str(), &results);
      igraph_vector_destroy(&results);
    } break;
    case IGRAPH_ATTRIBUTE_STRING: {
      igraph_strvector_t strresults;
      igraph_strvector_init(&strresults, 1);
      ar(make_nvp("edge_attr_" + attributeName, strresults));
      igraph_cattribute_EAS_setv(&graph, attributeName.c_str(), &strresults);
      igraph_strvector_destroy(&strresults);
    } break;
    default:
      throw std::runtime_error("This attribute type (" +
                               std::to_string(attributeType) +
                               ") is not supported");
    }
  }
  igraph_vector_int_destroy(&etypes);
  igraph_strvector_destroy(&enames);
}
} // namespace cereal

字符串

相关问题