root/isimud/exchanges/BATSBOE/common/processing_rules_impl.hpp

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


DEFINITIONS

This source file includes following definitions.
  1. process_msg
  2. to_string
  3. process_msg
  4. to_string
  5. decltype
  6. process_msg
  7. to_string

   1 /******************************************************************************
   2 ** $Header: svn+ssh://jmmcg@svn.code.sf.net/p/libjmmcg/code/trunk/libjmmcg/isimud/exchanges/BATSBOE/common/processing_rules_impl.hpp 2358 2018-10-22 23:59:23Z jmmcg $
   3 **
   4 ** Copyright (c) 2015 by J.M.McGuiness, isimud@hussar.me.uk
   5 **
   6 ** This library is free software; you can redistribute it and/or
   7 ** modify it under the terms of the GNU Lesser General Public
   8 ** License as published by the Free Software Foundation; either
   9 ** version 2.1 of the License, or (at your option) any later version.
  10 **
  11 ** This library is distributed in the hope that it will be useful,
  12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14 ** Lesser General Public License for more details.
  15 **
  16 ** You should have received a copy of the GNU Lesser General Public
  17 ** License along with this library; if not, write to the Free Software
  18 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19 */
  20 
  21 #include <functional>
  22 
  23 namespace isimud { namespace exchanges { namespace BATSBOE { namespace common {
  24 
  25 template<class SrcMsgDetails, class DestMsgDetails, class SktT>
  26 struct client_to_exchange_transformations<SrcMsgDetails, DestMsgDetails, SktT>::state_machine_t : jmmcg::msm::unroll::state_transition_table<state_machine_t> {
  27         using msm_base_t=jmmcg::msm::unroll::state_transition_table<state_machine_t>;
  28         using row_t=jmmcg::msm::unroll::row_types<typename src_msg_details_t::ExgchgMsgTypes, typename dest_msg_details_t::MsgTypes_t>;
  29         using transition_table=typename msm_base_t::template rows<
  30                 typename row_t::template row<
  31                         src_msg_details_t::NewOrder::type_const,
  32                         typename base_t::template convert_then_send_seq_num<typename src_msg_details_t::NewOrder, typename dest_msg_details_t::NewOrder>,
  33                         dest_msg_details_t::NewOrder::type_const
  34                 >,
  35                 typename row_t::template row<
  36                         src_msg_details_t::OrderCancelRequest::type_const,
  37                         typename base_t::template convert_then_send_seq_num<typename src_msg_details_t::OrderCancelRequest, typename dest_msg_details_t::OrderCancelRequest>,
  38                         dest_msg_details_t::OrderCancelRequest::type_const
  39                 >,
  40                 typename row_t::template row<
  41                         src_msg_details_t::OrderCancelReplace::type_const,
  42                         typename base_t::template convert_then_send_seq_num<typename src_msg_details_t::OrderCancelReplace, typename dest_msg_details_t::OrderCancelReplaceRequest>,
  43                         dest_msg_details_t::OrderCancelReplaceRequest::type_const
  44                 >,
  45                 /// Reject any message that has not been recognised.
  46                 typename row_t::template row<
  47                         src_msg_details_t::MatchAll,
  48                         typename base_t::template send_reject<typename src_msg_details_t::Reject, true>,
  49                         dest_msg_details_t::OrderRejected::type_const
  50                 >
  51         >;
  52 };
  53 
  54 template<class SrcMsgDetails, class DestMsgDetails, class SktT>
  55 inline bool
  56 client_to_exchange_transformations<SrcMsgDetails, DestMsgDetails, SktT>::process_msg(typename src_msg_details_t::msg_buffer_t const &buff, socket_t &exchg_skt, socket_t &client_skt) {
  57         ++sequenceNumber;
  58         typename src_msg_details_t::Header_t const &hdr=reinterpret_cast<typename src_msg_details_t::Header_t const &>(buff);
  59         const auto last_state=msm.process(hdr.type(), buff, exchg_skt, client_skt);
  60         return last_state==dest_msg_details_t::Exit;
  61 }
  62 
  63 template<class SrcMsgDetails, class DestMsgDetails, class SktT> inline std::string
  64 client_to_exchange_transformations<SrcMsgDetails, DestMsgDetails, SktT>::to_string() const noexcept(false) {
  65         std::ostringstream ss;
  66         ss
  67                 <<"\n\tCurrent sequence number="<<sequenceNumber
  68                 <<"Source message details: ";
  69         SrcMsgDetails::to_stream(ss);
  70         ss<<"\n\tDestination message details: ";
  71         DestMsgDetails::to_stream(ss);
  72         return ss.str();
  73 }
  74 
  75 template<class SrcMsgDetails, class DestMsgDetails, class SktT> inline std::ostream &
  76 operator<<(std::ostream &os, client_to_exchange_transformations<SrcMsgDetails, DestMsgDetails, SktT> const &ec) noexcept(false) {
  77         os<<ec.to_string();
  78         return os;
  79 }
  80 
  81 template<class SrcMsgDetails, class DestMsgDetails, class SktT>
  82 struct exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails, SktT>::state_machine_t : jmmcg::msm::unroll::state_transition_table<state_machine_t> {
  83         using msm_base_t=jmmcg::msm::unroll::state_transition_table<state_machine_t>;
  84         using row_t=jmmcg::msm::unroll::row_types<typename src_msg_details_t::MsgTypes_t, typename dest_msg_details_t::ExgchgMsgTypes>;
  85         using transition_table=typename msm_base_t::template rows<
  86                 /**
  87                         From section 2.4: "Heartbeats" of [1]: the response to a server Heartbeat is a Heartbeat.
  88 
  89                         \param  msg     The message that was received, that shall be processed.
  90                         \param exchg_skt        The socket to which any responses should be written.
  91                         \return False to continue processing messages, true otherwise.
  92                 */
  93                 typename row_t::template row<
  94                         src_msg_details_t::ServerHeartbeat::type_const,
  95                         typename base_t::template convert_then_send_seq_num<typename src_msg_details_t::ClientHeartbeat, typename src_msg_details_t::ClientHeartbeat, false>,
  96                         dest_msg_details_t::ClientHeartbeat::type_const
  97                 >,
  98                 /**
  99                         From section 2.5: "Logging Out" of [1]: the response to a Logout is to stop processing.
 100 
 101                         \param  msg     The message that was received, that shall be processed.
 102                         \param client_skt       The socket to which any responses should be written.
 103                         \return False to continue processing messages, true otherwise.
 104                 */
 105                 typename row_t::template row<
 106                         src_msg_details_t::Logout::type_const,
 107                         typename msm_base_t::no_op, // TODO
 108                         dest_msg_details_t::Exit
 109                 >,
 110                 typename row_t::template row<
 111                         src_msg_details_t::OrderRejected::type_const,
 112                         typename base_t::template convert_then_send<typename src_msg_details_t::OrderRejected, typename dest_msg_details_t::OrderRejected>,
 113                         dest_msg_details_t::OrderRejected::type_const
 114                 >,
 115                 typename row_t::template row<
 116                         src_msg_details_t::OrderCancelReject::type_const,
 117                         typename base_t::template convert_then_send<typename src_msg_details_t::OrderCancelReject, typename dest_msg_details_t::CancelRejected>,
 118                         dest_msg_details_t::CancelRejected::type_const
 119                 >,
 120                 typename row_t::template row<
 121                         src_msg_details_t::UserModifyRejected::type_const,
 122                         typename base_t::template convert_then_send<typename src_msg_details_t::UserModifyRejected, typename dest_msg_details_t::BusinessMessageReject>,
 123                         dest_msg_details_t::BusinessMessageReject::type_const
 124                 >,
 125                 typename row_t::template row<
 126                         src_msg_details_t::OrderCancelled::type_const,
 127                         typename base_t::template convert_then_send<typename src_msg_details_t::OrderCancelled, typename dest_msg_details_t::ExecutionReport>,
 128                         dest_msg_details_t::ExecutionReport::type_const
 129                 >,
 130                 typename row_t::template row<
 131                         src_msg_details_t::ExecutionReport::type_const,
 132                         typename base_t::template convert_then_send<typename src_msg_details_t::ExecutionReport, typename dest_msg_details_t::ExecutionReport>,
 133                         dest_msg_details_t::ExecutionReport::type_const
 134                 >,
 135                 /// Reject any message that has not been recognised.
 136                 typename row_t::template row<
 137                         src_msg_details_t::MatchAll,
 138                         typename base_t::template send_reject<typename dest_msg_details_t::Reject, true>,
 139                         dest_msg_details_t::Reject::type_const
 140                 >
 141         >;
 142 };
 143 
 144 template<class SrcMsgDetails, class DestMsgDetails, class SktT>
 145 inline bool
 146 exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails, SktT>::process_msg(typename src_msg_details_t::msg_buffer_t const &buff, socket_t &exchg_skt, socket_t &client_skt) {
 147         ++sequenceNumber;
 148         typename src_msg_details_t::Header_t const &hdr=reinterpret_cast<typename src_msg_details_t::Header_t const &>(buff);
 149         const auto last_state=msm.process(static_cast<typename src_msg_details_t::MsgTypes_t>(hdr.type()), buff, exchg_skt, client_skt);
 150         return last_state==dest_msg_details_t::Exit;
 151 }
 152 
 153 template<class SrcMsgDetails, class DestMsgDetails, class SktT> inline std::string
 154 exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails, SktT>::to_string() const noexcept(false) {
 155         std::ostringstream os;
 156         os<<"Source message details: ";
 157         SrcMsgDetails::to_stream(os);
 158         os<<"\n\tDestination message details: ";
 159         DestMsgDetails::to_stream(os);
 160         return os.str();
 161 }
 162 
 163 template<class SrcMsgDetails, class DestMsgDetails, class SktT> inline std::ostream &
 164 operator<<(std::ostream &os, exchange_to_client_transformations<SrcMsgDetails, DestMsgDetails, SktT> const &ec) noexcept(false) {
 165         os<<ec.to_string();
 166         return os;
 167 }
 168 
 169 template<class SrcMsgDetails, class SktT>
 170 struct simulator_responses<SrcMsgDetails, SktT>::state_machine_t : jmmcg::msm::unroll::state_transition_table<state_machine_t> {
 171         using msm_base_t=jmmcg::msm::unroll::state_transition_table<state_machine_t>;
 172         using row_t=jmmcg::msm::unroll::row_types<typename msg_details_t::MsgTypes_t, typename msg_details_t::MsgTypes_t>;
 173         class LogonRequestResponse {
 174         public:
 175                 explicit LogonRequestResponse(simulator_responses &sr) noexcept(true)
 176                 : sim_resp(sr) {}
 177                 constexpr LogonRequestResponse(LogonRequestResponse const &v) noexcept(true)
 178                 : sim_resp(v.sim_resp) {}
 179 
 180                 /**
 181                         From section 5.1: "Establishing a connection" of [1]: the response to a LogonRequest is a LogonReply.
 182 
 183                         \return The next state from the declaration of the table.
 184                 */
 185                 template<auto state, auto next>
 186                 auto operator()(typename msg_details_t::msg_buffer_t const &buff, socket_t &, socket_t &client_skt) const noexcept(false) {
 187                         typename msg_details_t::LogonRequest const &msg=reinterpret_cast<typename msg_details_t::LogonRequest const &>(buff);
 188                         assert(hdr.type()==state);
 189                         typename msg_details_t::LogonReply reply(sim_resp.sequenceNumber);
 190                         if (std::strcmp(msg.userName.begin(), sim_resp.username.begin())==0) {
 191                                 if (std::strcmp(msg.password.begin(), sim_resp.password.begin())==0) {
 192                                         reply.rejectCode(msg_details_t::LoginResponseStatus::LoginAccepted);
 193                                         client_skt.write(reply);
 194                                 } else {
 195                                         reply.rejectCode(msg_details_t::LoginResponseStatus::NotAuthorized);
 196                                         client_skt.write(reply);
 197                                 }
 198                         } else {
 199                                 reply.rejectCode(msg_details_t::LoginResponseStatus::NotAuthorized);
 200                                 client_skt.write(reply);
 201                         }
 202                         return next;
 203                 }
 204 
 205         private:
 206                 simulator_responses &sim_resp;
 207         };
 208         class LogoutRequestResponse {
 209         public:
 210                 explicit LogoutRequestResponse(simulator_responses &sr) noexcept(true)
 211                 : sim_resp(sr) {}
 212                 constexpr LogoutRequestResponse(LogoutRequestResponse const &v) noexcept(true)
 213                 : sim_resp(v.sim_resp) {}
 214 
 215                 /**
 216                         From section 5.3: "Terminating a connection" of [1]: the response to a Logout is a Logout.
 217 
 218                         \return The next state from the declaration of the table.
 219                 */
 220                 template<auto state, auto next>
 221                 auto operator()(typename msg_details_t::msg_buffer_t const &, socket_t &, socket_t &client_skt) const noexcept(false) {
 222                         const typename msg_details_t::Logout reply(sim_resp.sequenceNumber, LogoutReason::UserRequested);
 223                         client_skt.write(reply);
 224                         return next;
 225                 }
 226 
 227         private:
 228                 simulator_responses &sim_resp;
 229         };
 230         class OrderCancelRequestResponse {
 231         public:
 232                 explicit OrderCancelRequestResponse(simulator_responses &sr) noexcept(true)
 233                 : sim_resp(sr) {}
 234                 constexpr OrderCancelRequestResponse(OrderCancelRequestResponse const &v) noexcept(true)
 235                 : sim_resp(v.sim_resp) {}
 236 
 237                 /**
 238                         From section 9.1 "Order handling" of [1]: the response to a OrderCancelRequest.
 239 
 240                         \return The next state from the declaration of the table.
 241                 */
 242                 template<auto state, auto>
 243                 typename msg_details_t::MsgTypes_t
 244                 operator()(typename msg_details_t::msg_buffer_t const &buff, socket_t &, socket_t &client_skt) const noexcept(false) {
 245                         typename msg_details_t::OrderCancelRequest const &msg=reinterpret_cast<typename msg_details_t::OrderCancelRequest const &>(buff);
 246                         assert(hdr.type()==state);
 247                         assert(!std::string(msg.originalClientOrderID().data()).empty());
 248                         auto cancelled_order=sim_resp.order_book.find(msg.originalClientOrderID());
 249                         if (cancelled_order!=sim_resp.order_book.end()) {
 250                                 const typename msg_details_t::OrderCancelled reply(
 251                                         sim_resp.sequenceNumber,
 252                                         msg.originalClientOrderID(),
 253                                         msg_details_t::OrderRejectReason::UserRequested,
 254                                         cancelled_order->second.limitPrice(),
 255                                         cancelled_order->second.side(),
 256                                         0,
 257                                         cancelled_order->second.orderQty()
 258                                 );
 259                                 sim_resp.order_book.erase(msg.originalClientOrderID());
 260                                 client_skt.write(reply);
 261                                 return msg_details_t::OrderCancelled::type_const;
 262                         } else {
 263                                 const typename msg_details_t::OrderCancelReject reply(sim_resp.sequenceNumber, msg.originalClientOrderID(), msg_details_t::OrderRejectReason::ClOrdIDNotMatchKnownOrder);
 264                                 client_skt.write(reply);
 265                                 return msg_details_t::OrderCancelReject::type_const;
 266                         }
 267                 }
 268 
 269         private:
 270                 simulator_responses &sim_resp;
 271         };
 272         class OrderCancelReplaceRequestResponse {
 273         public:
 274                 explicit OrderCancelReplaceRequestResponse(simulator_responses &sr) noexcept(true)
 275                 : sim_resp(sr) {}
 276                 constexpr OrderCancelReplaceRequestResponse(OrderCancelReplaceRequestResponse const &v) noexcept(true)
 277                 : sim_resp(v.sim_resp) {}
 278 
 279                 /**
 280                         From section 9.1 "Order handling" of [1]: the response to a OrderCancelReplaceRequest.
 281 
 282                         \return The next state from the declaration of the table.
 283                 */
 284                 template<auto state, auto>
 285                 typename msg_details_t::MsgTypes_t
 286                 operator()(typename msg_details_t::msg_buffer_t const &buff, socket_t &, socket_t &client_skt) const noexcept(false) {
 287                         typename msg_details_t::OrderCancelReplaceRequest const &msg=reinterpret_cast<typename msg_details_t::OrderCancelReplaceRequest const &>(buff);
 288                         assert(hdr.type()==state);
 289                         auto modified_order=sim_resp.order_book.find(msg.originalClientOrderID());
 290                         if (modified_order!=sim_resp.order_book.end()) {
 291                                 modified_order->second.limitPrice(msg.limitPrice());
 292                                 modified_order->second.orderQty(msg.orderQty());
 293                                 modified_order->second.side(msg.side());
 294                                 const typename msg_details_t::OrderModified reply(
 295                                         sim_resp.sequenceNumber,
 296                                         msg.originalClientOrderID(),
 297                                         modified_order->second.limitPrice(),
 298                                         modified_order->second.side(),
 299                                         modified_order->second.orderQty()
 300                                 );
 301                                 client_skt.write(reply);
 302                                 return msg_details_t::OrderModified::type_const;
 303                         } else {
 304                                 const typename msg_details_t::UserModifyRejected reply(sim_resp.sequenceNumber, msg.originalClientOrderID(), OrderRejectReason::ClOrdIDNotMatchKnownOrder);
 305                                 client_skt.write(reply);
 306                                 return msg_details_t::UserModifyRejected::type_const;
 307                         }
 308                 }
 309 
 310         private:
 311                 simulator_responses &sim_resp;
 312         };
 313         class NewOrderResponse {
 314         public:
 315                 explicit NewOrderResponse(simulator_responses &sr) noexcept(true)
 316                 : sim_resp(sr) {}
 317                 constexpr NewOrderResponse(NewOrderResponse const &v) noexcept(true)
 318                 : sim_resp(v.sim_resp) {}
 319 
 320                 /**
 321                         From section 9.1 "Order handling" of [1]: the response to a NewOrder.
 322 
 323                         \return The next state from the declaration of the table.
 324                 */
 325                 template<auto state, auto>
 326                 typename msg_details_t::MsgTypes_t
 327                 operator()(typename msg_details_t::msg_buffer_t const &buff, socket_t &, socket_t &client_skt) const noexcept(false) {
 328                         typename msg_details_t::NewOrder const &msg=reinterpret_cast<typename msg_details_t::NewOrder const &>(buff);
 329                         assert(hdr.type()==state);
 330 
 331                         if (msg.instrumentID()==sim_resp.instrumentID) {
 332                                 typename msg_details_t::ExecutionReport reply(
 333                                         sim_resp.sequenceNumber,
 334                                         msg.clientOrderID(),
 335                                         sim_resp.scaled_price,
 336                                         msg.instrumentID(),
 337                                         msg.side()
 338                                 );
 339                                 if (msg.orderQty()<=sim_resp.quantity_limit) {
 340                                         if (msg.orderType()==msg_details_t::OrderType::Market
 341                                                 || (msg.side()==msg_details_t::Side::Buy && msg.limitPrice()<sim_resp.scaled_price)
 342                                                 || (msg.side()==msg_details_t::Side::Sell && msg.limitPrice()>sim_resp.scaled_price)) {
 343                                                 reply.executedQty(msg.orderQty());
 344                                                 reply.leavesQty(0);
 345                                                 reply.executedPrice(msg.limitPrice());
 346                                                 client_skt.write(reply);
 347                                         } else {
 348                                                 sim_resp.order_book.emplace(msg.clientOrderID(), msg);
 349                                                 assert(!sim_resp.order_book.empty());
 350                                                 assert(sim_resp.order_book.find(msg.clientOrderID())!=sim_resp.order_book.end());
 351                                         }
 352                                 } else {
 353                                         auto new_item=sim_resp.order_book.emplace(msg.clientOrderID(), msg);
 354                                         new_item.first->second.orderQty(msg.orderQty()-sim_resp.quantity_limit);
 355                                         reply.executedQty(sim_resp.quantity_limit);
 356                                         reply.leavesQty(msg.orderQty()-sim_resp.quantity_limit);
 357                                         assert(!sim_resp.order_book.empty());
 358                                         assert(sim_resp.order_book.find(msg.clientOrderID())!=sim_resp.order_book.end());
 359                                         client_skt.write(reply);
 360                                 }
 361                                 return msg_details_t::ExecutionReport::type_const;
 362                         } else {
 363                                 const typename msg_details_t::OrderRejected reply(sim_resp.sequenceNumber, msg.clientOrderID(), msg_details_t::OrderRejectReason::SymbolNotSupported);
 364                                 client_skt.write(reply);
 365                                 return msg_details_t::OrderRejected::type_const;
 366                         }
 367                 }
 368 
 369         private:
 370                 simulator_responses &sim_resp;
 371         };
 372         class RejectUnhandledResponse {
 373         public:
 374                 explicit RejectUnhandledResponse(simulator_responses &sr) noexcept(true)
 375                 : sim_resp(sr) {}
 376                 constexpr RejectUnhandledResponse(RejectUnhandledResponse const &v) noexcept(true)
 377                 : sim_resp(v.sim_resp) {}
 378 
 379                 /**
 380                         \param client_skt       The socket to which any responses should be written.
 381                         \return The next state from the declaration of the table.
 382                 */
 383                 template<auto state, auto next>
 384                 decltype(next)
 385                 operator()(typename src_msg_details_t::msg_buffer_t const &, socket_t &, socket_t &client_skt) const {
 386                         const typename msg_details_t::OrderRejected response(sim_resp.sequenceNumber, typename src_msg_details_t::ClientOrderID_t(), src_msg_details_t::OrderRejected::unknown_msg);
 387                         client_skt.write(response);
 388                         return next;
 389                 }
 390 
 391         private:
 392                 simulator_responses &sim_resp;
 393         };
 394         using transition_table=typename msm_base_t::template rows<
 395                 typename row_t::template row<
 396                         msg_details_t::LogonRequest::type_const,
 397                         LogonRequestResponse,
 398                         msg_details_t::LogonReply::type_const
 399                 >,
 400                 /**
 401                         From section 2.4: "Heartbeats" of [1]: the response to a Heartbeat is nothing.
 402                 */
 403                 typename row_t::template row<
 404                         msg_details_t::ClientHeartbeat::type_const,
 405                         typename msm_base_t::no_op,
 406                         msg_details_t::ServerHeartbeat::type_const
 407                 >,
 408                 typename row_t::template row<
 409                         msg_details_t::LogoutRequest::type_const,
 410                         LogoutRequestResponse,
 411                         msg_details_t::Exit
 412                 >,
 413                 typename row_t::template row<
 414                         msg_details_t::OrderCancelRequest::type_const,
 415                         OrderCancelRequestResponse,
 416                         msg_details_t::OrderCancelled::type_const|msg_details_t::OrderCancelReject::type_const
 417                 >,
 418                 typename row_t::template row<
 419                         msg_details_t::OrderCancelReplaceRequest::type_const,
 420                         OrderCancelReplaceRequestResponse,
 421                         msg_details_t::OrderModified::type_const|msg_details_t::UserModifyRejected::type_const
 422                 >,
 423                 typename row_t::template row<
 424                         msg_details_t::NewOrder::type_const,
 425                         NewOrderResponse,
 426                         msg_details_t::ExecutionReport::type_const|msg_details_t::OrderRejected::type_const
 427                 >,
 428                 /// Reject any message that has not been recognised.
 429                 typename row_t::template row<
 430                         msg_details_t::MatchAll,
 431                         RejectUnhandledResponse,
 432                         msg_details_t::OrderRejected::type_const
 433                 >
 434         >;
 435 };
 436 
 437 template<class SrcMsgDetails, class SktT>
 438 inline bool
 439 simulator_responses<SrcMsgDetails, SktT>::process_msg(typename msg_details_t::msg_buffer_t const &buff, socket_t &exchg_skt, socket_t &client_skt) {
 440         ++(this->sequenceNumber);
 441         typename msg_details_t::Header_t const &hdr=reinterpret_cast<typename msg_details_t::Header_t const &>(buff);
 442         const auto last_state=msm.process(static_cast<typename msg_details_t::MsgTypes_t>(hdr.type()), buff, exchg_skt, client_skt);
 443         return last_state==msg_details_t::Exit;
 444 }
 445 
 446 template<class SrcMsgDetails, class SktT> inline std::string
 447 simulator_responses<SrcMsgDetails, SktT>::to_string() const noexcept(false) {
 448         std::ostringstream os;
 449         os
 450                 <<base_t::to_string()
 451                 <<"\n\tValid instrument ID="<<instrumentID
 452                 <<"\n\tInvalid instrument ID="<<invalidInstrumentID;
 453         return os.str();
 454 }
 455 
 456 template<class SrcMsgDetails, class SktT> inline std::ostream &
 457 operator<<(std::ostream &os, simulator_responses<SrcMsgDetails, SktT> const &ec) noexcept(false) {
 458         os<<ec.to_string();
 459         return os;
 460 }
 461 
 462 } } } }

/* [<][>][^][v][top][bottom][index][help] */