提升业力:对transform_attribute的这种隐式调用是如何工作的? (或者不是吗?)

时间:2016-06-20 15:11:13

标签: c++ c++11 boost boost-spirit-karma

我有以下代码似乎工作正常(我基于reuse parsed variable with boost karma上的语义操作)。

#include <iostream>
#include <iterator>
#include <memory>
#include <string>
#include <vector>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/sequence.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/support_attributes.hpp>
#include <boost/spirit/include/support_adapt_adt_attributes.hpp>

using namespace boost::spirit;

struct DataElement
{
  DataElement(const std::string& s) : str_(s) {}

  const std::string& str() const { return str_; }
  std::string& str() { return str_; }
  std::string str_;
};
using Data = std::vector<std::shared_ptr<const DataElement>>;

namespace boost {
  namespace spirit {
    namespace traits {

      template<>
      struct transform_attribute<std::shared_ptr<const DataElement> const, const DataElement&, karma::domain>
      {
        using type = const DataElement&;
        static type pre(const std::shared_ptr<const DataElement>& val) { return *val; }
      };

    }
  }
}

BOOST_FUSION_ADAPT_ADT(
  DataElement,
  (std::string&, const std::string&, obj.str(), obj.str())
  );

template<typename Iterator>
struct TheGrammar: public karma::grammar<Iterator, Data()>
{
  TheGrammar(): karma::grammar<Iterator, Data()>(start)
  {
    start %= -(elt % karma::eol);
    elt %=
      karma::lit("'some prefix'")
      << karma::string [karma::_1 = boost::phoenix::at_c<0>(karma::_val)]
      << karma::lit("'some infix 1'")
      << karma::string [karma::_1 = boost::phoenix::at_c<0>(karma::_val)]
      << karma::lit("'some infix 2'")
      << karma::string [karma::_1 = boost::phoenix::at_c<0>(karma::_val)]
      << karma::lit("'some suffix'")
      ;
  }

  karma::rule<Iterator, Data()> start;
  karma::rule<Iterator, const DataElement&()> elt;
};

int main(void)
{
  Data vec = {
    std::make_shared<DataElement>("one"),
    std::make_shared<DataElement>("two"),
    std::make_shared<DataElement>("three"),
    std::make_shared<DataElement>("four"),
    std::make_shared<DataElement>("five"),
    std::make_shared<DataElement>("six"),
    std::make_shared<DataElement>("seven"),
    std::make_shared<DataElement>("eight"),
  };
  using iterator_type = std::ostream_iterator<char>;
  iterator_type out(std::cout);

  TheGrammar<iterator_type> grammar;
  return karma::generate(out, grammar, vec);
}

我想了解一些事情:

  1. 为什么我不需要在任何地方使用karma::attr_cast?我的start规则是std::shared_ptr的向量,而elt规则适用于实际的对象const引用。我最初尝试attr_cast但没有任何地方,只是为了防止它工作,只是半心半意地尝试了这个版本,并且它有效......
  2. 如果我完全注释掉我的自定义transform_attribute,为什么它仍然可以编译?是否存在默认std::shared_ptr<T> - &gt;提供了T& transform_attribute?我找不到多少东西,但也许我没有找到合适的地方?
  3. 如果我注释掉我的自定义transform_attribute,如上所述,代码仍然编译,但运行时显然有一些内存损坏。 karma::string生成垃圾。在某种程度上,我可以理解,有趣的事情一定要发生,因为我甚至不会告诉业力如何从我的shared_ptr到达对象。它是否编译了实际的错误/错误?
  4. 非常感谢您的时间和帮助!

1 个答案:

答案 0 :(得分:1)

  1. 每个规则都具有对声明的属性类型
  2. 的隐式attr_cast
  3. 一直以来,Spirit的类型兼容性规则变得混乱。我所看到的只是它与字符串类型是容器这一事实有关。在某个地方,它复制构造&#34;一个似乎长度为97332352的std :: string。不出所料,这本身就是错误的并且碰巧触发了UB,因为最终传递给memset的范围重叠:

    Source and destination overlap in memcpy(0x60c1040, 0x5cd2c90, 97332352)
       at 0x4C30573: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
       by 0x401B26: copy (char_traits.h:290)
       by 0x401B26: _S_copy (basic_string.h:299)
       by 0x401B26: _S_copy_chars (basic_string.h:342)
       by 0x401B26: void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char*>(char*, char*, std::forward_iterator_tag) [clone .isra.53] (basic_string.tcc:229)
       by 0x402442: _M_construct_aux<char*> (basic_string.h:195)
       by 0x402442: _M_construct<char*> (basic_string.h:214)
       by 0x402442: basic_string (basic_string.h:401)
       by 0x402442: call<const boost::spirit::unused_type> (extract_from.hpp:172)
       by 0x402442: call<const boost::spirit::unused_type> (extract_from.hpp:184)
       by 0x402442: extract_from<std::__cxx11::basic_string<char>, boost::fusion::extension::adt_attribute_proxy<DataElement, 0, true>, const boost::spirit::unused_type> (extract_from.hpp:217)
       by 0x402442: extract_from<std::__cxx11::basic_string<char>, boost::fusion::extension::adt_attribute_proxy<DataElement, 0, true>, const boost::spirit::unused_type> (extract_from.hpp:237)
       by 0x402442: pre (attributes.hpp:23)
    
  4. 是的,这是一个QoI问题。

    问题通常在于c ++的隐式转换。指针类型有许多意外的转换。共享指针的上下文转换为bool。

  5. 更多说明:

    1. 您的融合修改似乎存在缺陷:val未在二传手中使用

      BOOST_FUSION_ADAPT_ADT(DataElement, (std::string &, const std::string &, obj.str(), obj.str() = val))
      
    2. 你做了许多我学会避免的事情。