void t_cpp_generator::generate_process_function()

in compiler/cpp/src/thrift/generate/t_cpp_generator.cc [3552:3962]


void t_cpp_generator::generate_process_function(t_service* tservice,
                                                t_function* tfunction,
                                                string style,
                                                bool specialized) {
  t_struct* arg_struct = tfunction->get_arglist();
  const std::vector<t_field*>& fields = arg_struct->get_members();
  vector<t_field*>::const_iterator f_iter;

  t_struct* xs = tfunction->get_xceptions();
  const std::vector<t_field*>& xceptions = xs->get_members();
  vector<t_field*>::const_iterator x_iter;
  string service_func_name = "\"" + tservice->get_name() + "." + tfunction->get_name() + "\"";

  std::ostream& out = (gen_templates_ ? f_service_tcc_ : f_service_);

  string prot_type = (specialized ? "Protocol_" : "::apache::thrift::protocol::TProtocol");
  string class_suffix;
  if (gen_templates_) {
    class_suffix = "T<Protocol_>";
  }

  // I tried to do this as one function.  I really did.  But it was too hard.
  if (style != "Cob") {
    // Open function
    if (gen_templates_) {
      out << indent() << "template <class Protocol_>" << '\n';
    }
    const bool unnamed_oprot_seqid = tfunction->is_oneway() && !(gen_templates_ && !specialized);
    out << "void " << tservice->get_name() << "Processor" << class_suffix << "::"
        << "process_" << tfunction->get_name() << "("
        << "int32_t" << (unnamed_oprot_seqid ? ", " : " seqid, ") << prot_type << "* iprot, "
        << prot_type << "*" << (unnamed_oprot_seqid ? ", " : " oprot, ") << "void* callContext)"
        << '\n';
    scope_up(out);

    string argsname = tservice->get_name() + "_" + tfunction->get_name() + "_args";
    string resultname = tservice->get_name() + "_" + tfunction->get_name() + "_result";

    if (tfunction->is_oneway() && !unnamed_oprot_seqid) {
      out << indent() << "(void) seqid;" << '\n' << indent() << "(void) oprot;" << '\n';
    }

    out << indent() << "void* ctx = nullptr;" << '\n' << indent()
        << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
        << "  ctx = this->eventHandler_->getContext(" << service_func_name << ", callContext);"
        << '\n' << indent() << "}" << '\n' << indent()
        << "::apache::thrift::TProcessorContextFreer freer("
        << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << '\n' << '\n'
        << indent() << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
        << "  this->eventHandler_->preRead(ctx, " << service_func_name << ");" << '\n' << indent()
        << "}" << '\n' << '\n' << indent() << argsname << " args;" << '\n' << indent()
        << "args.read(iprot);" << '\n' << indent() << "iprot->readMessageEnd();" << '\n' << indent()
        << "uint32_t bytes = iprot->getTransport()->readEnd();" << '\n' << '\n' << indent()
        << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
        << "  this->eventHandler_->postRead(ctx, " << service_func_name << ", bytes);" << '\n'
        << indent() << "}" << '\n' << '\n';

    // Declare result
    if (!tfunction->is_oneway()) {
      out << indent() << resultname << " result;" << '\n';
    }

    // Try block for functions with exceptions
    out << indent() << "try {" << '\n';
    indent_up();

    // Generate the function call
    bool first = true;
    out << indent();
    if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
      if (is_complex_type(tfunction->get_returntype())) {
        first = false;
        out << "iface_->" << tfunction->get_name() << "(result.success";
      } else {
        out << "result.success = iface_->" << tfunction->get_name() << "(";
      }
    } else {
      out << "iface_->" << tfunction->get_name() << "(";
    }
    for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
      if (first) {
        first = false;
      } else {
        out << ", ";
      }
      out << "args." << (*f_iter)->get_name();
    }
    out << ");" << '\n';

    // Set isset on success field
    if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) {
      out << indent() << "result.__isset.success = true;" << '\n';
    }

    indent_down();
    out << indent() << "}";

    if (!tfunction->is_oneway()) {
      for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
        out << " catch (" << type_name((*x_iter)->get_type()) << " &" << (*x_iter)->get_name()
            << ") {" << '\n';
        if (!tfunction->is_oneway()) {
          indent_up();
          out << indent() << "result." << (*x_iter)->get_name()
                          << " = std::move(" << (*x_iter)->get_name() << ");" << '\n'
              << indent() << "result.__isset." << (*x_iter)->get_name() << " = true;" << '\n';
          indent_down();
          out << indent() << "}";
        } else {
          out << "}";
        }
      }
    }

    if (!tfunction->is_oneway()) {
      out << " catch (const std::exception& e) {" << '\n';
    } else {
      out << " catch (const std::exception&) {" << '\n';
    }

    indent_up();
    out << indent() << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
        << "  this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << '\n'
        << indent() << "}" << '\n';

    if (!tfunction->is_oneway()) {
      out << '\n' << indent() << "::apache::thrift::TApplicationException x(e.what());" << '\n'
          << indent() << "oprot->writeMessageBegin(\"" << tfunction->get_name()
          << "\", ::apache::thrift::protocol::T_EXCEPTION, seqid);" << '\n' << indent()
          << "x.write(oprot);" << '\n' << indent() << "oprot->writeMessageEnd();" << '\n'
          << indent() << "oprot->getTransport()->writeEnd();" << '\n' << indent()
          << "oprot->getTransport()->flush();" << '\n';
    }
    out << indent() << "return;" << '\n';
    indent_down();
    out << indent() << "}" << '\n' << '\n';

    // Shortcut out here for oneway functions
    if (tfunction->is_oneway()) {
      out << indent() << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
          << "  this->eventHandler_->asyncComplete(ctx, " << service_func_name << ");" << '\n'
          << indent() << "}" << '\n' << '\n' << indent() << "return;" << '\n';
      indent_down();
      out << "}" << '\n' << '\n';
      return;
    }

    // Serialize the result into a struct
    out << indent() << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
        << "  this->eventHandler_->preWrite(ctx, " << service_func_name << ");" << '\n' << indent()
        << "}" << '\n' << '\n' << indent() << "oprot->writeMessageBegin(\"" << tfunction->get_name()
        << "\", ::apache::thrift::protocol::T_REPLY, seqid);" << '\n' << indent()
        << "result.write(oprot);" << '\n' << indent() << "oprot->writeMessageEnd();" << '\n'
        << indent() << "bytes = oprot->getTransport()->writeEnd();" << '\n' << indent()
        << "oprot->getTransport()->flush();" << '\n' << '\n' << indent()
        << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
        << "  this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << '\n'
        << indent() << "}" << '\n';

    // Close function
    scope_down(out);
    out << '\n';
  }

  // Cob style.
  else {
    // Processor entry point.
    // TODO(edhall) update for callContext when TEventServer is ready
    if (gen_templates_) {
      out << indent() << "template <class Protocol_>" << '\n';
    }
    out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::process_"
        << tfunction->get_name() << "(::std::function<void(bool ok)> cob, int32_t seqid, "
        << prot_type << "* iprot, " << prot_type << "* oprot)" << '\n';
    scope_up(out);

    // TODO(simpkins): we could try to consoldate this
    // with the non-cob code above
    if (gen_templates_ && !specialized) {
      // If these are instances of Protocol_, instead of any old TProtocol,
      // use the specialized process function instead.
      out << indent() << "Protocol_* _iprot = dynamic_cast<Protocol_*>(iprot);" << '\n' << indent()
          << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" << '\n' << indent()
          << "if (_iprot && _oprot) {" << '\n' << indent() << "  return process_"
          << tfunction->get_name() << "(cob, seqid, _iprot, _oprot);" << '\n' << indent() << "}"
          << '\n' << indent() << "T_GENERIC_PROTOCOL(this, iprot, _iprot);" << '\n' << indent()
          << "T_GENERIC_PROTOCOL(this, oprot, _oprot);" << '\n' << '\n';
    }

    if (tfunction->is_oneway()) {
      out << indent() << "(void) seqid;" << '\n' << indent() << "(void) oprot;" << '\n';
    }

    out << indent() << tservice->get_name() + "_" + tfunction->get_name() << "_args args;" << '\n'
        << indent() << "void* ctx = nullptr;" << '\n' << indent()
        << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
        << "  ctx = this->eventHandler_->getContext(" << service_func_name << ", nullptr);" << '\n'
        << indent() << "}" << '\n' << indent() << "::apache::thrift::TProcessorContextFreer freer("
        << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << '\n' << '\n'
        << indent() << "try {" << '\n';
    indent_up();
    out << indent() << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
        << "  this->eventHandler_->preRead(ctx, " << service_func_name << ");" << '\n' << indent()
        << "}" << '\n' << indent() << "args.read(iprot);" << '\n' << indent()
        << "iprot->readMessageEnd();" << '\n' << indent()
        << "uint32_t bytes = iprot->getTransport()->readEnd();" << '\n' << indent()
        << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
        << "  this->eventHandler_->postRead(ctx, " << service_func_name << ", bytes);" << '\n'
        << indent() << "}" << '\n';
    scope_down(out);

    // TODO(dreiss): Handle TExceptions?  Expose to server?
    out << indent() << "catch (const std::exception&) {" << '\n' << indent()
        << "  if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
        << "    this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << '\n'
        << indent() << "  }" << '\n' << indent() << "  return cob(false);" << '\n' << indent()
        << "}" << '\n';

    if (tfunction->is_oneway()) {
      out << indent() << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
          << "  this->eventHandler_->asyncComplete(ctx, " << service_func_name << ");" << '\n'
          << indent() << "}" << '\n';
    }
    // TODO(dreiss): Figure out a strategy for exceptions in async handlers.
    out << indent() << "freer.unregister();" << '\n';
    if (tfunction->is_oneway()) {
      // No return.  Just hand off our cob.
      // TODO(dreiss): Call the cob immediately?
      out << indent() << "iface_->" << tfunction->get_name() << "("
          << "::std::bind(cob, true)" << '\n';
      indent_up();
      indent_up();
    } else {
      string ret_arg, ret_placeholder;
      if (!tfunction->get_returntype()->is_void()) {
        ret_arg = ", const " + type_name(tfunction->get_returntype()) + "& _return";
        ret_placeholder = ", ::std::placeholders::_1";
      }

      // When gen_templates_ is true, the return_ and throw_ functions are
      // overloaded.  We have to declare pointers to them so that the compiler
      // can resolve the correct overloaded version.
      out << indent() << "void (" << tservice->get_name() << "AsyncProcessor" << class_suffix
          << "::*return_fn)(::std::function<void(bool ok)> "
          << "cob, int32_t seqid, " << prot_type << "* oprot, void* ctx" << ret_arg
          << ") =" << '\n';
      out << indent() << "  &" << tservice->get_name() << "AsyncProcessor" << class_suffix
          << "::return_" << tfunction->get_name() << ";" << '\n';
      if (!xceptions.empty()) {
        out << indent() << "void (" << tservice->get_name() << "AsyncProcessor" << class_suffix
            << "::*throw_fn)(::std::function<void(bool ok)> "
            << "cob, int32_t seqid, " << prot_type << "* oprot, void* ctx, "
            << "::apache::thrift::TDelayedException* _throw) =" << '\n';
        out << indent() << "  &" << tservice->get_name() << "AsyncProcessor" << class_suffix
            << "::throw_" << tfunction->get_name() << ";" << '\n';
      }

      out << indent() << "iface_->" << tfunction->get_name() << "(" << '\n';
      indent_up();
      indent_up();
      out << indent() << "::std::bind(return_fn, this, cob, seqid, oprot, ctx" << ret_placeholder
          << ")";
      if (!xceptions.empty()) {
        out << ',' << '\n' << indent() << "::std::bind(throw_fn, this, cob, seqid, oprot, "
            << "ctx, ::std::placeholders::_1)";
      }
    }

    // XXX Whitespace cleanup.
    for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
      out << ',' << '\n' << indent() << "args." << (*f_iter)->get_name();
    }
    out << ");" << '\n';
    indent_down();
    indent_down();
    scope_down(out);
    out << '\n';

    // Normal return.
    if (!tfunction->is_oneway()) {
      string ret_arg_decl, ret_arg_name;
      if (!tfunction->get_returntype()->is_void()) {
        ret_arg_decl = ", const " + type_name(tfunction->get_returntype()) + "& _return";
        ret_arg_name = ", _return";
      }
      if (gen_templates_) {
        out << indent() << "template <class Protocol_>" << '\n';
      }
      out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::return_"
          << tfunction->get_name() << "(::std::function<void(bool ok)> cob, int32_t seqid, "
          << prot_type << "* oprot, void* ctx" << ret_arg_decl << ')' << '\n';
      scope_up(out);

      if (gen_templates_ && !specialized) {
        // If oprot is a Protocol_ instance,
        // use the specialized return function instead.
        out << indent() << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" << '\n'
            << indent() << "if (_oprot) {" << '\n' << indent() << "  return return_"
            << tfunction->get_name() << "(cob, seqid, _oprot, ctx" << ret_arg_name << ");" << '\n'
            << indent() << "}" << '\n' << indent() << "T_GENERIC_PROTOCOL(this, oprot, _oprot);"
            << '\n' << '\n';
      }

      out << indent() << tservice->get_name() << "_" << tfunction->get_name() << "_presult result;"
          << '\n';
      if (!tfunction->get_returntype()->is_void()) {
        // The const_cast here is unfortunate, but it would be a pain to avoid,
        // and we only do a write with this struct, which is const-safe.
        out << indent() << "result.success = const_cast<" << type_name(tfunction->get_returntype())
            << "*>(&_return);" << '\n' << indent() << "result.__isset.success = true;" << '\n';
      }
      // Serialize the result into a struct
      out << '\n' << indent() << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
          << "  ctx = this->eventHandler_->getContext(" << service_func_name << ", nullptr);" << '\n'
          << indent() << "}" << '\n' << indent()
          << "::apache::thrift::TProcessorContextFreer freer("
          << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << '\n' << '\n'
          << indent() << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
          << "  this->eventHandler_->preWrite(ctx, " << service_func_name << ");" << '\n'
          << indent() << "}" << '\n' << '\n' << indent() << "oprot->writeMessageBegin(\""
          << tfunction->get_name() << "\", ::apache::thrift::protocol::T_REPLY, seqid);" << '\n'
          << indent() << "result.write(oprot);" << '\n' << indent() << "oprot->writeMessageEnd();"
          << '\n' << indent() << "uint32_t bytes = oprot->getTransport()->writeEnd();" << '\n'
          << indent() << "oprot->getTransport()->flush();" << '\n' << indent()
          << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
          << "  this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << '\n'
          << indent() << "}" << '\n' << indent() << "return cob(true);" << '\n';
      scope_down(out);
      out << '\n';
    }

    // Exception return.
    if (!tfunction->is_oneway() && !xceptions.empty()) {
      if (gen_templates_) {
        out << indent() << "template <class Protocol_>" << '\n';
      }
      out << "void " << tservice->get_name() << "AsyncProcessor" << class_suffix << "::throw_"
          << tfunction->get_name() << "(::std::function<void(bool ok)> cob, int32_t seqid, "
          << prot_type << "* oprot, void* ctx, "
          << "::apache::thrift::TDelayedException* _throw)" << '\n';
      scope_up(out);

      if (gen_templates_ && !specialized) {
        // If oprot is a Protocol_ instance,
        // use the specialized throw function instead.
        out << indent() << "Protocol_* _oprot = dynamic_cast<Protocol_*>(oprot);" << '\n'
            << indent() << "if (_oprot) {" << '\n' << indent() << "  return throw_"
            << tfunction->get_name() << "(cob, seqid, _oprot, ctx, _throw);" << '\n' << indent()
            << "}" << '\n' << indent() << "T_GENERIC_PROTOCOL(this, oprot, _oprot);" << '\n'
            << '\n';
      }

      // Get the event handler context
      out << '\n' << indent() << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
          << "  ctx = this->eventHandler_->getContext(" << service_func_name << ", nullptr);" << '\n'
          << indent() << "}" << '\n' << indent()
          << "::apache::thrift::TProcessorContextFreer freer("
          << "this->eventHandler_.get(), ctx, " << service_func_name << ");" << '\n' << '\n';

      // Throw the TDelayedException, and catch the result
      out << indent() << tservice->get_name() << "_" << tfunction->get_name() << "_result result;"
          << '\n' << '\n' << indent() << "try {" << '\n';
      indent_up();
      out << indent() << "_throw->throw_it();" << '\n' << indent() << "return cob(false);"
          << '\n'; // Is this possible?  TBD.
      indent_down();
      out << indent() << '}';
      for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
        out << "  catch (" << type_name((*x_iter)->get_type()) << " &" << (*x_iter)->get_name()
            << ") {" << '\n';
        indent_up();
        out << indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name()
            << ";" << '\n' << indent() << "result.__isset." << (*x_iter)->get_name() << " = true;"
            << '\n';
        scope_down(out);
      }

      // Handle the case where an undeclared exception is thrown
      out << " catch (std::exception& e) {" << '\n';
      indent_up();
      out << indent() << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
          << "  this->eventHandler_->handlerError(ctx, " << service_func_name << ");" << '\n'
          << indent() << "}" << '\n' << '\n' << indent()
          << "::apache::thrift::TApplicationException x(e.what());" << '\n' << indent()
          << "oprot->writeMessageBegin(\"" << tfunction->get_name()
          << "\", ::apache::thrift::protocol::T_EXCEPTION, seqid);" << '\n' << indent()
          << "x.write(oprot);" << '\n' << indent() << "oprot->writeMessageEnd();" << '\n'
          << indent() << "oprot->getTransport()->writeEnd();" << '\n' << indent()
          << "oprot->getTransport()->flush();" << '\n' <<
          // We pass true to the cob here, since we did successfully write a
          // response, even though it is an exception response.
          // It looks like the argument is currently ignored, anyway.
          indent() << "return cob(true);" << '\n';
      scope_down(out);

      // Serialize the result into a struct
      out << indent() << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
          << "  this->eventHandler_->preWrite(ctx, " << service_func_name << ");" << '\n'
          << indent() << "}" << '\n' << '\n' << indent() << "oprot->writeMessageBegin(\""
          << tfunction->get_name() << "\", ::apache::thrift::protocol::T_REPLY, seqid);" << '\n'
          << indent() << "result.write(oprot);" << '\n' << indent() << "oprot->writeMessageEnd();"
          << '\n' << indent() << "uint32_t bytes = oprot->getTransport()->writeEnd();" << '\n'
          << indent() << "oprot->getTransport()->flush();" << '\n' << indent()
          << "if (this->eventHandler_.get() != nullptr) {" << '\n' << indent()
          << "  this->eventHandler_->postWrite(ctx, " << service_func_name << ", bytes);" << '\n'
          << indent() << "}" << '\n' << indent() << "return cob(true);" << '\n';
      scope_down(out);
      out << '\n';
    } // for each function
  }   // cob style
}