Повысить карму: как работает этот неявный вызов transform_attribute? (или нет?)
У меня есть следующий фрагмент кода, который, кажется, работает нормально (я основал семантические действия на повторном использовании анализируемой переменной с boost кармой).
#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);
}
Я хотел бы понять пару вещей:
- Почему мне не нужно использовать
karma::attr_cast
в любом месте? мойstart
Правило это векторstd::shared_ptr
тогда какelt
правило действует на фактическую ссылку на объект const. Я изначально пробовалattr_cast
но не получилось, и вроде как попробовал эту версию только от всего сердца, на случай, если она сработает, и сработало... - Почему он все еще компилируется, если я закомментирую свой обычай
transform_attribute
вообще? Есть ли какой-то по умолчаниюstd::shared_ptr<T>
->T&
transform_attribute предоставлен? Я не мог найти много, но, возможно, я не смотрю в нужном месте? - Если я закомментирую свой обычай
transform_attribute
Как уже упоминалось выше, код все еще скомпилирован, но во время выполнения явно наблюдается некоторое повреждение памяти.karma::string
генерировать мусор. В некотором смысле, я могу понять, что должно происходить что-то смешное, так как я даже не говорю карме, как получить от моегоshared_ptr
к объектам. Является ли факт, что он компилирует фактическую ошибку / ошибку?
Большое спасибо за ваше время и помощь!
1 ответ
- каждое правило имеет неявное attr_cast для объявленного типа атрибута
Что-то пошло не так, как правила совместимости типов Spirit потерпели неудачу. Все, что я видел, это то, что строковый тип является контейнером. Где-то на этом пути он "копирует-конструирует" std:: string, которая, кажется, имеет длину 97332352. Неудивительно, что это само по себе неверно и вызывает 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)
Да, это проблема QoI.
Проблема часто заключается в неявных преобразованиях C++. Типы указателей имеют много неожиданных преобразований. Общие указатели имеют свое контекстное преобразование в bool.
Больше примечаний:
Ваша адаптация к фьюжн казалась ошибочной
val
не использовался в сеттереBOOST_FUSION_ADAPT_ADT(DataElement, (std::string &, const std::string &, obj.str(), obj.str() = val))
Вы делаете много вещей, которых я научился избегать.
- Я предпочитаю иметь правила без семантических действий: Boost Spirit: "Семантические действия - это зло"?
- Я не делю общие указатели в грамматиках / генераторах Spirit. Как я могу использовать полиморфные атрибуты с парсерами boost::spirit::qi? (возможно, в настройках генератора это меньше проблем!)
- Я не занимаюсь адаптацией ADT (слишком легко укусить с помощью UB) Ошибка при адаптации класса с помощью BOOST_FUSION_ADAPT_ADT