В Google+ увидел ссылку вот на это небольшое обсуждение. Немного улыбнуло. По-моему, в Рунете заговорили об использовании DSL уже довольно давно, никак не позже 2007-го. Вспомнил про свою статью, которую в 2008-м написал как раз для того, чтобы поделиться опытом использования DSL на Ruby для облегчения одной из моих тогдашних задач: Пример использования Ruby для генерации C++ кода в библиотеке разбора EMI протокола.
Статья довольно большая. Я в ней не делал явного акцента на том, в чем именно выгода от DSL. В статье рассказывается обо всем понемножку: от исходной задачи к аргументам в пользу DSL и альтернативам, а так же немного деталей устройства самого DSL и его использования. Так что ответ на вопрос "А в чем именно выгода от DSL" нужно будет искать в нескольких ее местах.
Тем же, кому читать много букв некогда, попробую выразить сухой остаток кратко, проиллюстрировав фрагментами кода.
Выгода в том, что разработчику дается возможность очень лаконично описать то, что ему нужно. А потом, посредством совсем не сложного инструмента (который легко осваивается и сопровождается) это описание транслируется в то, чем разработчик может пользоваться.
Упомянутый в статье DSL использовался для описания структуры пакетов прикладного протокола UCP/EMI. Из этого описания затем генерировались C++классы для работы с этими пакетами в C++ программах. Пример описания одного пакета и фрагментов сгенерированного кода находятся под катом.
Пример описания структуры одного семейства пакетов UCP/EMI на языке Ruby:
require 'emi_pdu_1/pdu_generator' pdu_class :operation_base_t do |pdu| pdu.decl_file :script_relative => 'ucp5x.operation.hpp.inl' pdu.impl_file :script_relative => '../ucp5x.operation.cpp.inl' pdu.field :AdC, num_char( 1, 16 ), :presence => :mandatory pdu.field :OAdC, any_char( 22 ), :presence => :mandatory, :visibility => :protected pdu.field :AC, num_char( 1, 16 ) pdu.field :NRq, left_padded_uint( 1 ) pdu.field :NAdC, num_char( 1, 16 ) pdu.field :NT, left_padded_uint( 1 ) pdu.field :NPID, num_char( 4 ) pdu.field :LRq, num_char( 1 ) pdu.field :LRAd, num_char( 1, 16 ) pdu.field :LPID, num_char( 4 ) pdu.field :DD, num_char( 1 ) pdu.field :DDT, num_char( 10 ) pdu.field :VP, num_char( 10 ) pdu.field :RPID, num_char( 4 ) pdu.field :SCTS, num_char( 12 ) pdu.field :Dst, left_padded_uint( 1 ) pdu.field :Rsn, num_char( 3 ) pdu.field :DSCTS, num_char( 12 ) pdu.field :MT, left_padded_uint( 1 ), :presence => :mandatory, :visibility => :protected pdu.field :NB, num_char( 1, 4 ), :visibility => :protected pdu.field :Msg, hex_char( 640 ), :visibility => :protected pdu.field :MMS, num_char( 1 ) pdu.field :PR, any_char( 1 ) pdu.field :DCs, left_padded_uint( 1 ) pdu.field :MCLs, left_padded_uint( 1 ) pdu.field :PRI, num_char( 1 ) pdu.field :CPg, num_char( 1 ) pdu.field :RPLy, num_char( 1 ) pdu.field :OTOA, num_char( 4 ) pdu.field :HPLMN, num_char( 1, 16 ) pdu.field :XSer, hex_char( 400 ) pdu.field :RES4, num_char( 128 ) pdu.field :RES5, num_char( 128 ) end |
Фрагмент описания C++ класса, сгенерированного из Ruby-нового DSL:
class operation_base_t : public fields_bunch_t { public : operation_base_t(); virtual ~operation_base_t(); /*! * \name Реализация интерфейса fields_bunch. * \{ */ virtual void encode( oess_1::io::ostream_t & to ) const; virtual void decode( const unclassified_fields_t & fields ); virtual void debug_dump( std::ostream & to ) const; /*! * \} */ /*! * \name Методы доступа к открытым полям. * \{ */ //! Установлено ли значение AdC. bool is_AdC_defined() const; //! Сброс значения AdC. void drop_AdC(); //! Получение значения AdC. /*! * \throw ex_t если значение не установлено. */ const std::string & query_AdC() const; //! Получение значения AdC если оно определено, //! или значения по умолчанию в противном случае. std::string fetch_AdC( const std::string & default_value ) const; //! Установка значения AdC. void set_AdC( const std::string & v ); //! Установлено ли значение AC. bool is_AC_defined() const; //! Сброс значения AC. void drop_AC(); //! Получение значения AC. /*! * \throw ex_t если значение не установлено. */ const std::string & query_AC() const; //! Получение значения AC если оно определено, //! или значения по умолчанию в противном случае. std::string fetch_AC( const std::string & default_value ) const; //! Установка значения AC. void set_AC( const std::string & v ); |
Фрагмент реализации C++ класса, сгенерированного из Ruby-нового DSL:
operation_base_t::operation_base_t() : m_AdC( "AdC" ) , m_OAdC( "OAdC" ) , m_AC( "AC" ) , m_NRq( "NRq" ) , m_NAdC( "NAdC" ) , m_NT( "NT" ) , m_NPID( "NPID" ) , m_LRq( "LRq" ) , m_LRAd( "LRAd" ) , m_LPID( "LPID" ) , m_DD( "DD" ) , m_DDT( "DDT" ) , m_VP( "VP" ) , m_RPID( "RPID" ) , m_SCTS( "SCTS" ) , m_Dst( "Dst" ) , m_Rsn( "Rsn" ) , m_DSCTS( "DSCTS" ) , m_MT( "MT" ) , m_NB( "NB" ) , m_Msg( "Msg" ) , m_MMS( "MMS" ) , m_PR( "PR" ) , m_DCs( "DCs" ) , m_MCLs( "MCLs" ) , m_PRI( "PRI" ) , m_CPg( "CPg" ) , m_RPLy( "RPLy" ) , m_OTOA( "OTOA" ) , m_HPLMN( "HPLMN" ) , m_XSer( "XSer" ) , m_RES4( "RES4" ) , m_RES5( "RES5" ) {} operation_base_t::~operation_base_t() {} void operation_base_t::encode( oess_1::io::ostream_t & to ) const { m_AdC.encode( to ); to << fields_separator; m_OAdC.encode( to ); to << fields_separator; m_AC.encode( to ); to << fields_separator; m_NRq.encode( to ); to << fields_separator; m_NAdC.encode( to ); to << fields_separator; m_NT.encode( to ); to << fields_separator; m_NPID.encode( to ); to << fields_separator; m_LRq.encode( to ); to << fields_separator; m_LRAd.encode( to ); to << fields_separator; m_LPID.encode( to ); to << fields_separator; m_DD.encode( to ); to << fields_separator; m_DDT.encode( to ); to << fields_separator; m_VP.encode( to ); to << fields_separator; m_RPID.encode( to ); to << fields_separator; m_SCTS.encode( to ); to << fields_separator; m_Dst.encode( to ); to << fields_separator; m_Rsn.encode( to ); to << fields_separator; m_DSCTS.encode( to ); to << fields_separator; m_MT.encode( to ); to << fields_separator; m_NB.encode( to ); to << fields_separator; m_Msg.encode( to ); to << fields_separator; m_MMS.encode( to ); to << fields_separator; m_PR.encode( to ); to << fields_separator; m_DCs.encode( to ); to << fields_separator; m_MCLs.encode( to ); to << fields_separator; m_PRI.encode( to ); to << fields_separator; m_CPg.encode( to ); to << fields_separator; m_RPLy.encode( to ); to << fields_separator; m_OTOA.encode( to ); to << fields_separator; m_HPLMN.encode( to ); to << fields_separator; m_XSer.encode( to ); to << fields_separator; m_RES4.encode( to ); to << fields_separator; m_RES5.encode( to ); to << fields_separator; } void operation_base_t::decode( const unclassified_fields_t & fields ) { if( 33 != fields.fields_count() ) throw ex_t( "operation_base_t: pdu field count mismatch; " "expected count: 33; actual count: " + cpp_util_2::slexcast( fields.fields_count() ) ); m_AdC.decode( fields.data() + fields.field_at( 0 ).offset(), fields.field_at( 0 ).length() ); m_OAdC.decode( fields.data() + fields.field_at( 1 ).offset(), fields.field_at( 1 ).length() ); m_AC.decode( fields.data() + fields.field_at( 2 ).offset(), fields.field_at( 2 ).length() ); m_NRq.decode( fields.data() + fields.field_at( 3 ).offset(), fields.field_at( 3 ).length() ); m_NAdC.decode( fields.data() + fields.field_at( 4 ).offset(), fields.field_at( 4 ).length() ); m_NT.decode( fields.data() + fields.field_at( 5 ).offset(), fields.field_at( 5 ).length() ); m_NPID.decode( fields.data() + fields.field_at( 6 ).offset(), fields.field_at( 6 ).length() ); m_LRq.decode( fields.data() + fields.field_at( 7 ).offset(), fields.field_at( 7 ).length() ); m_LRAd.decode( fields.data() + fields.field_at( 8 ).offset(), fields.field_at( 8 ).length() ); m_LPID.decode( fields.data() + fields.field_at( 9 ).offset(), fields.field_at( 9 ).length() ); m_DD.decode( fields.data() + fields.field_at( 10 ).offset(), fields.field_at( 10 ).length() ); m_DDT.decode( fields.data() + fields.field_at( 11 ).offset(), fields.field_at( 11 ).length() ); m_VP.decode( fields.data() + fields.field_at( 12 ).offset(), fields.field_at( 12 ).length() ); m_RPID.decode( fields.data() + fields.field_at( 13 ).offset(), fields.field_at( 13 ).length() ); m_SCTS.decode( fields.data() + fields.field_at( 14 ).offset(), fields.field_at( 14 ).length() ); m_Dst.decode( fields.data() + fields.field_at( 15 ).offset(), fields.field_at( 15 ).length() ); m_Rsn.decode( fields.data() + fields.field_at( 16 ).offset(), fields.field_at( 16 ).length() ); m_DSCTS.decode( fields.data() + fields.field_at( 17 ).offset(), fields.field_at( 17 ).length() ); m_MT.decode( fields.data() + fields.field_at( 18 ).offset(), fields.field_at( 18 ).length() ); m_NB.decode( fields.data() + fields.field_at( 19 ).offset(), fields.field_at( 19 ).length() ); m_Msg.decode( fields.data() + fields.field_at( 20 ).offset(), fields.field_at( 20 ).length() ); m_MMS.decode( fields.data() + fields.field_at( 21 ).offset(), fields.field_at( 21 ).length() ); m_PR.decode( fields.data() + fields.field_at( 22 ).offset(), fields.field_at( 22 ).length() ); m_DCs.decode( fields.data() + fields.field_at( 23 ).offset(), fields.field_at( 23 ).length() ); m_MCLs.decode( fields.data() + fields.field_at( 24 ).offset(), fields.field_at( 24 ).length() ); m_PRI.decode( fields.data() + fields.field_at( 25 ).offset(), fields.field_at( 25 ).length() ); m_CPg.decode( fields.data() + fields.field_at( 26 ).offset(), fields.field_at( 26 ).length() ); m_RPLy.decode( fields.data() + fields.field_at( 27 ).offset(), fields.field_at( 27 ).length() ); m_OTOA.decode( fields.data() + fields.field_at( 28 ).offset(), fields.field_at( 28 ).length() ); m_HPLMN.decode( fields.data() + fields.field_at( 29 ).offset(), fields.field_at( 29 ).length() ); m_XSer.decode( fields.data() + fields.field_at( 30 ).offset(), fields.field_at( 30 ).length() ); m_RES4.decode( fields.data() + fields.field_at( 31 ).offset(), fields.field_at( 31 ).length() ); m_RES5.decode( fields.data() + fields.field_at( 32 ).offset(), fields.field_at( 32 ).length() ); } void operation_base_t::debug_dump( std::ostream & to ) const { to << m_AdC.field_name() << '='; if( m_AdC.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_AdC.value() ) << "\"/"; else to << '/'; to << m_OAdC.field_name() << '='; if( m_OAdC.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_OAdC.value() ) << "\"/"; else to << '/'; to << m_AC.field_name() << '='; if( m_AC.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_AC.value() ) << "\"/"; else to << '/'; to << m_NRq.field_name() << '='; if( m_NRq.is_defined() ) to << m_NRq.value() << '/'; else to << '/'; to << m_NAdC.field_name() << '='; if( m_NAdC.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_NAdC.value() ) << "\"/"; else to << '/'; to << m_NT.field_name() << '='; if( m_NT.is_defined() ) to << m_NT.value() << '/'; else to << '/'; to << m_NPID.field_name() << '='; if( m_NPID.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_NPID.value() ) << "\"/"; else to << '/'; to << m_LRq.field_name() << '='; if( m_LRq.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_LRq.value() ) << "\"/"; else to << '/'; to << m_LRAd.field_name() << '='; if( m_LRAd.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_LRAd.value() ) << "\"/"; else to << '/'; to << m_LPID.field_name() << '='; if( m_LPID.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_LPID.value() ) << "\"/"; else to << '/'; to << m_DD.field_name() << '='; if( m_DD.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_DD.value() ) << "\"/"; else to << '/'; to << m_DDT.field_name() << '='; if( m_DDT.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_DDT.value() ) << "\"/"; else to << '/'; to << m_VP.field_name() << '='; if( m_VP.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_VP.value() ) << "\"/"; else to << '/'; to << m_RPID.field_name() << '='; if( m_RPID.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_RPID.value() ) << "\"/"; else to << '/'; to << m_SCTS.field_name() << '='; if( m_SCTS.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_SCTS.value() ) << "\"/"; else to << '/'; to << m_Dst.field_name() << '='; if( m_Dst.is_defined() ) to << m_Dst.value() << '/'; else to << '/'; to << m_Rsn.field_name() << '='; if( m_Rsn.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_Rsn.value() ) << "\"/"; else to << '/'; to << m_DSCTS.field_name() << '='; if( m_DSCTS.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_DSCTS.value() ) << "\"/"; else to << '/'; to << m_MT.field_name() << '='; if( m_MT.is_defined() ) to << m_MT.value() << '/'; else to << '/'; to << m_NB.field_name() << '='; if( m_NB.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_NB.value() ) << "\"/"; else to << '/'; to << m_Msg.field_name() << '='; if( m_Msg.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_Msg.value() ) << "\"/"; else to << '/'; to << m_MMS.field_name() << '='; if( m_MMS.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_MMS.value() ) << "\"/"; else to << '/'; to << m_PR.field_name() << '='; if( m_PR.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_PR.value() ) << "\"/"; else to << '/'; to << m_DCs.field_name() << '='; if( m_DCs.is_defined() ) to << m_DCs.value() << '/'; else to << '/'; to << m_MCLs.field_name() << '='; if( m_MCLs.is_defined() ) to << m_MCLs.value() << '/'; else to << '/'; to << m_PRI.field_name() << '='; if( m_PRI.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_PRI.value() ) << "\"/"; else to << '/'; to << m_CPg.field_name() << '='; if( m_CPg.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_CPg.value() ) << "\"/"; else to << '/'; to << m_RPLy.field_name() << '='; if( m_RPLy.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_RPLy.value() ) << "\"/"; else to << '/'; to << m_OTOA.field_name() << '='; if( m_OTOA.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_OTOA.value() ) << "\"/"; else to << '/'; to << m_HPLMN.field_name() << '='; if( m_HPLMN.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_HPLMN.value() ) << "\"/"; else to << '/'; to << m_XSer.field_name() << '='; if( m_XSer.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_XSer.value() ) << "\"/"; else to << '/'; to << m_RES4.field_name() << '='; if( m_RES4.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_RES4.value() ) << "\"/"; else to << '/'; to << m_RES5.field_name() << '='; if( m_RES5.is_defined() ) to << '"' << cpp_util_2::hex_dumps::hex_escaped_string_dumper( m_RES5.value() ) << "\"/"; else to << '/'; } bool operation_base_t::is_AdC_defined() const { return m_AdC.is_defined(); } void operation_base_t::drop_AdC() { m_AdC.drop(); } const std::string & operation_base_t::query_AdC() const { return m_AdC.value(); } std::string operation_base_t::fetch_AdC( const std::string & default_value ) const { if( m_AdC.is_defined() ) return m_AdC.value(); else return default_value; } void operation_base_t::set_AdC( const std::string & v ) { m_AdC.set( v ); } |
Комментариев нет:
Отправить комментарий