Skip to content
Snippets Groups Projects
  • Per Grön's avatar
    b50cf568
    Emit additional headers in generated .h file instead of .cc · b50cf568
    Per Grön authored
    For Flatbuffers compatibility.
    
    From what I can tell, File::additional_headers is not used by gRPC
    itself or its default protobuf implementation; it was added for
    Flatbuffers support (it just returns "" for protobuf).
    
    In the Flatbuffer case, the generated header contains references to
    Flatbuffer gRPC glue code which is in a header in additional_headers.
    Prior to this patch, this meant that the generated .h file could not
    be included unless this glue file was included first.
    
    Because the protobuf implementation of additional_headers returns
    an empty string, I think this change should be safe to do and not
    have unintentional consequences.
    b50cf568
    History
    Emit additional headers in generated .h file instead of .cc
    Per Grön authored
    For Flatbuffers compatibility.
    
    From what I can tell, File::additional_headers is not used by gRPC
    itself or its default protobuf implementation; it was added for
    Flatbuffers support (it just returns "" for protobuf).
    
    In the Flatbuffer case, the generated header contains references to
    Flatbuffer gRPC glue code which is in a header in additional_headers.
    Prior to this patch, this meant that the generated .h file could not
    be included unless this glue file was included first.
    
    Because the protobuf implementation of additional_headers returns
    an empty string, I think this change should be safe to do and not
    have unintentional consequences.
cpp_generator.cc 61.44 KiB
/*
 *
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <map>

#include "src/compiler/cpp_generator.h"

#include <sstream>

namespace grpc_cpp_generator {
namespace {

template <class T>
grpc::string as_string(T x) {
  std::ostringstream out;
  out << x;
  return out.str();
}

inline bool ClientOnlyStreaming(const grpc_generator::Method *method) {
  return method->ClientStreaming() && !method->ServerStreaming();
}

inline bool ServerOnlyStreaming(const grpc_generator::Method *method) {
  return !method->ClientStreaming() && method->ServerStreaming();
}

grpc::string FilenameIdentifier(const grpc::string &filename) {
  grpc::string result;
  for (unsigned i = 0; i < filename.size(); i++) {
    char c = filename[i];
    if (isalnum(c)) {
      result.push_back(c);
    } else {
      static char hex[] = "0123456789abcdef";
      result.push_back('_');
      result.push_back(hex[(c >> 4) & 0xf]);
      result.push_back(hex[c & 0xf]);
    }
  }
  return result;
}
}  // namespace

template <class T, size_t N>
T *array_end(T (&array)[N]) {
  return array + N;
}

void PrintIncludes(grpc_generator::Printer *printer,
                   const std::vector<grpc::string> &headers,
                   const Parameters &params) {
  std::map<grpc::string, grpc::string> vars;

  vars["l"] = params.use_system_headers ? '<' : '"';
  vars["r"] = params.use_system_headers ? '>' : '"';

  auto &s = params.grpc_search_path;
  if (!s.empty()) {
    vars["l"] += s;
    if (s[s.size() - 1] != '/') {
      vars["l"] += '/';
    }
  }

  for (auto i = headers.begin(); i != headers.end(); i++) {
    vars["h"] = *i;
    printer->Print(vars, "#include $l$$h$$r$\n");
  }
}

grpc::string GetHeaderPrologue(grpc_generator::File *file,
                               const Parameters & /*params*/) {
  grpc::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(&output);
    std::map<grpc::string, grpc::string> vars;

    vars["filename"] = file->filename();
    vars["filename_identifier"] = FilenameIdentifier(file->filename());
    vars["filename_base"] = file->filename_without_ext();
    vars["message_header_ext"] = kCppGeneratorMessageHeaderExt;

    printer->Print(vars, "// Generated by the gRPC C++ plugin.\n");
    printer->Print(vars,
                   "// If you make any local change, they will be lost.\n");
    printer->Print(vars, "// source: $filename$\n");
    grpc::string leading_comments = file->GetLeadingComments("//");
    if (!leading_comments.empty()) {
      printer->Print(vars, "// Original file comments:\n");
      printer->Print(leading_comments.c_str());
    }
    printer->Print(vars, "#ifndef GRPC_$filename_identifier$__INCLUDED\n");
    printer->Print(vars, "#define GRPC_$filename_identifier$__INCLUDED\n");
    printer->Print(vars, "\n");
    printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n");
    printer->Print(vars, file->additional_headers().c_str());
    printer->Print(vars, "\n");
  }
  return output;
}

grpc::string GetHeaderIncludes(grpc_generator::File *file,
                               const Parameters &params) {
  grpc::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(&output);
    std::map<grpc::string, grpc::string> vars;

    static const char *headers_strs[] = {
        "grpc++/impl/codegen/async_stream.h",
        "grpc++/impl/codegen/async_unary_call.h",
        "grpc++/impl/codegen/method_handler_impl.h",
        "grpc++/impl/codegen/proto_utils.h",
        "grpc++/impl/codegen/rpc_method.h",
        "grpc++/impl/codegen/service_type.h",
        "grpc++/impl/codegen/status.h",
        "grpc++/impl/codegen/stub_options.h",
        "grpc++/impl/codegen/sync_stream.h"};
    std::vector<grpc::string> headers(headers_strs, array_end(headers_strs));
    PrintIncludes(printer.get(), headers, params);
    printer->Print(vars, "\n");
    printer->Print(vars, "namespace grpc {\n");
    printer->Print(vars, "class CompletionQueue;\n");
    printer->Print(vars, "class Channel;\n");
    printer->Print(vars, "class RpcService;\n");
    printer->Print(vars, "class ServerCompletionQueue;\n");
    printer->Print(vars, "class ServerContext;\n");
    printer->Print(vars, "}  // namespace grpc\n\n");

    if (!file->package().empty()) {
      std::vector<grpc::string> parts = file->package_parts();

      for (auto part = parts.begin(); part != parts.end(); part++) {
        vars["part"] = *part;
        printer->Print(vars, "namespace $part$ {\n");
      }
      printer->Print(vars, "\n");
    }
  }
  return output;
}

void PrintHeaderClientMethodInterfaces(
    grpc_generator::Printer *printer, const grpc_generator::Method *method,
    std::map<grpc::string, grpc::string> *vars, bool is_public) {
  (*vars)["Method"] = method->name();
  (*vars)["Request"] = method->input_type_name();
  (*vars)["Response"] = method->output_type_name();

  if (is_public) {
    if (method->NoStreaming()) {
      printer->Print(
          *vars,
          "virtual ::grpc::Status $Method$(::grpc::ClientContext* context, "
          "const $Request$& request, $Response$* response) = 0;\n");
      printer->Print(*vars,
                     "std::unique_ptr< "
                     "::grpc::ClientAsyncResponseReaderInterface< $Response$>> "
                     "Async$Method$(::grpc::ClientContext* context, "
                     "const $Request$& request, "
                     "::grpc::CompletionQueue* cq) {\n");
      printer->Indent();
      printer->Print(*vars,
                     "return std::unique_ptr< "
                     "::grpc::ClientAsyncResponseReaderInterface< $Response$>>("
                     "Async$Method$Raw(context, request, cq));\n");
      printer->Outdent();
      printer->Print("}\n");
    } else if (ClientOnlyStreaming(method)) {
      printer->Print(
          *vars,
          "std::unique_ptr< ::grpc::ClientWriterInterface< $Request$>>"
          " $Method$("
          "::grpc::ClientContext* context, $Response$* response) {\n");
      printer->Indent();
      printer->Print(
          *vars,
          "return std::unique_ptr< ::grpc::ClientWriterInterface< $Request$>>"
          "($Method$Raw(context, response));\n");
      printer->Outdent();
      printer->Print("}\n");
      printer->Print(
          *vars,
          "std::unique_ptr< ::grpc::ClientAsyncWriterInterface< $Request$>>"
          " Async$Method$(::grpc::ClientContext* context, $Response$* "
          "response, "
          "::grpc::CompletionQueue* cq, void* tag) {\n");
      printer->Indent();
      printer->Print(*vars,
                     "return std::unique_ptr< "
                     "::grpc::ClientAsyncWriterInterface< $Request$>>("
                     "Async$Method$Raw(context, response, cq, tag));\n");
      printer->Outdent();
      printer->Print("}\n");
    } else if (ServerOnlyStreaming(method)) {
      printer->Print(
          *vars,
          "std::unique_ptr< ::grpc::ClientReaderInterface< $Response$>>"
          " $Method$(::grpc::ClientContext* context, const $Request$& request)"
          " {\n");
      printer->Indent();
      printer->Print(
          *vars,
          "return std::unique_ptr< ::grpc::ClientReaderInterface< $Response$>>"
          "($Method$Raw(context, request));\n");
      printer->Outdent();
      printer->Print("}\n");
      printer->Print(
          *vars,
          "std::unique_ptr< ::grpc::ClientAsyncReaderInterface< $Response$>> "
          "Async$Method$("
          "::grpc::ClientContext* context, const $Request$& request, "
          "::grpc::CompletionQueue* cq, void* tag) {\n");
      printer->Indent();
      printer->Print(*vars,
                     "return std::unique_ptr< "
                     "::grpc::ClientAsyncReaderInterface< $Response$>>("
                     "Async$Method$Raw(context, request, cq, tag));\n");
      printer->Outdent();
      printer->Print("}\n");
    } else if (method->BidiStreaming()) {
      printer->Print(*vars,
                     "std::unique_ptr< ::grpc::ClientReaderWriterInterface< "
                     "$Request$, $Response$>> "
                     "$Method$(::grpc::ClientContext* context) {\n");
      printer->Indent();
      printer->Print(
          *vars,
          "return std::unique_ptr< "
          "::grpc::ClientReaderWriterInterface< $Request$, $Response$>>("
          "$Method$Raw(context));\n");
      printer->Outdent();
      printer->Print("}\n");
      printer->Print(
          *vars,
          "std::unique_ptr< "
          "::grpc::ClientAsyncReaderWriterInterface< $Request$, $Response$>> "
          "Async$Method$(::grpc::ClientContext* context, "
          "::grpc::CompletionQueue* cq, void* tag) {\n");
      printer->Indent();
      printer->Print(
          *vars,
          "return std::unique_ptr< "
          "::grpc::ClientAsyncReaderWriterInterface< $Request$, $Response$>>("
          "Async$Method$Raw(context, cq, tag));\n");
      printer->Outdent();
      printer->Print("}\n");
    }
  } else {
    if (method->NoStreaming()) {
      printer->Print(
          *vars,
          "virtual ::grpc::ClientAsyncResponseReaderInterface< $Response$>* "
          "Async$Method$Raw(::grpc::ClientContext* context, "
          "const $Request$& request, "
          "::grpc::CompletionQueue* cq) = 0;\n");
    } else if (ClientOnlyStreaming(method)) {
      printer->Print(
          *vars,
          "virtual ::grpc::ClientWriterInterface< $Request$>*"
          " $Method$Raw("
          "::grpc::ClientContext* context, $Response$* response) = 0;\n");
      printer->Print(*vars,
                     "virtual ::grpc::ClientAsyncWriterInterface< $Request$>*"
                     " Async$Method$Raw(::grpc::ClientContext* context, "
                     "$Response$* response, "
                     "::grpc::CompletionQueue* cq, void* tag) = 0;\n");
    } else if (ServerOnlyStreaming(method)) {
      printer->Print(
          *vars,
          "virtual ::grpc::ClientReaderInterface< $Response$>* $Method$Raw("
          "::grpc::ClientContext* context, const $Request$& request) = 0;\n");
      printer->Print(
          *vars,
          "virtual ::grpc::ClientAsyncReaderInterface< $Response$>* "
          "Async$Method$Raw("
          "::grpc::ClientContext* context, const $Request$& request, "
          "::grpc::CompletionQueue* cq, void* tag) = 0;\n");
    } else if (method->BidiStreaming()) {
      printer->Print(*vars,
                     "virtual ::grpc::ClientReaderWriterInterface< $Request$, "
                     "$Response$>* "
                     "$Method$Raw(::grpc::ClientContext* context) = 0;\n");
      printer->Print(*vars,
                     "virtual ::grpc::ClientAsyncReaderWriterInterface< "
                     "$Request$, $Response$>* "
                     "Async$Method$Raw(::grpc::ClientContext* context, "
                     "::grpc::CompletionQueue* cq, void* tag) = 0;\n");
    }
  }
}

void PrintHeaderClientMethod(grpc_generator::Printer *printer,
                             const grpc_generator::Method *method,
                             std::map<grpc::string, grpc::string> *vars,
                             bool is_public) {
  (*vars)["Method"] = method->name();
  (*vars)["Request"] = method->input_type_name();
  (*vars)["Response"] = method->output_type_name();
  if (is_public) {
    if (method->NoStreaming()) {
      printer->Print(
          *vars,
          "::grpc::Status $Method$(::grpc::ClientContext* context, "
          "const $Request$& request, $Response$* response) override;\n");
      printer->Print(
          *vars,
          "std::unique_ptr< ::grpc::ClientAsyncResponseReader< $Response$>> "
          "Async$Method$(::grpc::ClientContext* context, "
          "const $Request$& request, "
          "::grpc::CompletionQueue* cq) {\n");
      printer->Indent();
      printer->Print(*vars,
                     "return std::unique_ptr< "
                     "::grpc::ClientAsyncResponseReader< $Response$>>("
                     "Async$Method$Raw(context, request, cq));\n");
      printer->Outdent();
      printer->Print("}\n");
    } else if (ClientOnlyStreaming(method)) {
      printer->Print(
          *vars,
          "std::unique_ptr< ::grpc::ClientWriter< $Request$>>"
          " $Method$("
          "::grpc::ClientContext* context, $Response$* response) {\n");
      printer->Indent();
      printer->Print(*vars,
                     "return std::unique_ptr< ::grpc::ClientWriter< $Request$>>"
                     "($Method$Raw(context, response));\n");
      printer->Outdent();
      printer->Print("}\n");
      printer->Print(*vars,
                     "std::unique_ptr< ::grpc::ClientAsyncWriter< $Request$>>"
                     " Async$Method$(::grpc::ClientContext* context, "
                     "$Response$* response, "
                     "::grpc::CompletionQueue* cq, void* tag) {\n");
      printer->Indent();
      printer->Print(
          *vars,
          "return std::unique_ptr< ::grpc::ClientAsyncWriter< $Request$>>("
          "Async$Method$Raw(context, response, cq, tag));\n");
      printer->Outdent();
      printer->Print("}\n");
    } else if (ServerOnlyStreaming(method)) {
      printer->Print(
          *vars,
          "std::unique_ptr< ::grpc::ClientReader< $Response$>>"
          " $Method$(::grpc::ClientContext* context, const $Request$& request)"
          " {\n");
      printer->Indent();
      printer->Print(
          *vars,
          "return std::unique_ptr< ::grpc::ClientReader< $Response$>>"
          "($Method$Raw(context, request));\n");
      printer->Outdent();
      printer->Print("}\n");
      printer->Print(
          *vars,
          "std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>> "
          "Async$Method$("
          "::grpc::ClientContext* context, const $Request$& request, "
          "::grpc::CompletionQueue* cq, void* tag) {\n");
      printer->Indent();
      printer->Print(
          *vars,
          "return std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>>("
          "Async$Method$Raw(context, request, cq, tag));\n");
      printer->Outdent();
      printer->Print("}\n");
    } else if (method->BidiStreaming()) {
      printer->Print(
          *vars,
          "std::unique_ptr< ::grpc::ClientReaderWriter< $Request$, $Response$>>"
          " $Method$(::grpc::ClientContext* context) {\n");
      printer->Indent();
      printer->Print(*vars,
                     "return std::unique_ptr< "
                     "::grpc::ClientReaderWriter< $Request$, $Response$>>("
                     "$Method$Raw(context));\n");
      printer->Outdent();
      printer->Print("}\n");
      printer->Print(*vars,
                     "std::unique_ptr<  ::grpc::ClientAsyncReaderWriter< "
                     "$Request$, $Response$>> "
                     "Async$Method$(::grpc::ClientContext* context, "
                     "::grpc::CompletionQueue* cq, void* tag) {\n");
      printer->Indent();
      printer->Print(*vars,
                     "return std::unique_ptr< "
                     "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>>("
                     "Async$Method$Raw(context, cq, tag));\n");
      printer->Outdent();
      printer->Print("}\n");
    }
  } else {
    if (method->NoStreaming()) {
      printer->Print(*vars,
                     "::grpc::ClientAsyncResponseReader< $Response$>* "
                     "Async$Method$Raw(::grpc::ClientContext* context, "
                     "const $Request$& request, "
                     "::grpc::CompletionQueue* cq) override;\n");
    } else if (ClientOnlyStreaming(method)) {
      printer->Print(*vars,
                     "::grpc::ClientWriter< $Request$>* $Method$Raw("
                     "::grpc::ClientContext* context, $Response$* response) "
                     "override;\n");
      printer->Print(*vars,
                     "::grpc::ClientAsyncWriter< $Request$>* Async$Method$Raw("
                     "::grpc::ClientContext* context, $Response$* response, "
                     "::grpc::CompletionQueue* cq, void* tag) override;\n");
    } else if (ServerOnlyStreaming(method)) {
      printer->Print(*vars,
                     "::grpc::ClientReader< $Response$>* $Method$Raw("
                     "::grpc::ClientContext* context, const $Request$& request)"
                     " override;\n");
      printer->Print(
          *vars,
          "::grpc::ClientAsyncReader< $Response$>* Async$Method$Raw("
          "::grpc::ClientContext* context, const $Request$& request, "
          "::grpc::CompletionQueue* cq, void* tag) override;\n");
    } else if (method->BidiStreaming()) {
      printer->Print(*vars,
                     "::grpc::ClientReaderWriter< $Request$, $Response$>* "
                     "$Method$Raw(::grpc::ClientContext* context) override;\n");
      printer->Print(*vars,
                     "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>* "
                     "Async$Method$Raw(::grpc::ClientContext* context, "
                     "::grpc::CompletionQueue* cq, void* tag) override;\n");
    }
  }
}

void PrintHeaderClientMethodData(grpc_generator::Printer *printer,
                                 const grpc_generator::Method *method,
                                 std::map<grpc::string, grpc::string> *vars) {
  (*vars)["Method"] = method->name();
  printer->Print(*vars, "const ::grpc::RpcMethod rpcmethod_$Method$_;\n");
}

void PrintHeaderServerMethodSync(grpc_generator::Printer *printer,
                                 const grpc_generator::Method *method,
                                 std::map<grpc::string, grpc::string> *vars) {
  (*vars)["Method"] = method->name();
  (*vars)["Request"] = method->input_type_name();
  (*vars)["Response"] = method->output_type_name();
  printer->Print(method->GetLeadingComments("//").c_str());
  if (method->NoStreaming()) {
    printer->Print(*vars,
                   "virtual ::grpc::Status $Method$("
                   "::grpc::ServerContext* context, const $Request$* request, "
                   "$Response$* response);\n");
  } else if (ClientOnlyStreaming(method)) {
    printer->Print(*vars,
                   "virtual ::grpc::Status $Method$("
                   "::grpc::ServerContext* context, "
                   "::grpc::ServerReader< $Request$>* reader, "
                   "$Response$* response);\n");
  } else if (ServerOnlyStreaming(method)) {
    printer->Print(*vars,
                   "virtual ::grpc::Status $Method$("
                   "::grpc::ServerContext* context, const $Request$* request, "
                   "::grpc::ServerWriter< $Response$>* writer);\n");
  } else if (method->BidiStreaming()) {
    printer->Print(
        *vars,
        "virtual ::grpc::Status $Method$("
        "::grpc::ServerContext* context, "
        "::grpc::ServerReaderWriter< $Response$, $Request$>* stream);"
        "\n");
  }
  printer->Print(method->GetTrailingComments("//").c_str());
}

void PrintHeaderServerMethodAsync(grpc_generator::Printer *printer,
                                  const grpc_generator::Method *method,
                                  std::map<grpc::string, grpc::string> *vars) {
  (*vars)["Method"] = method->name();
  (*vars)["Request"] = method->input_type_name();
  (*vars)["Response"] = method->output_type_name();
  printer->Print(*vars, "template <class BaseClass>\n");
  printer->Print(*vars,
                 "class WithAsyncMethod_$Method$ : public BaseClass {\n");
  printer->Print(
      " private:\n"
      "  void BaseClassMustBeDerivedFromService(const Service *service) {}\n");
  printer->Print(" public:\n");
  printer->Indent();
  printer->Print(*vars,
                 "WithAsyncMethod_$Method$() {\n"
                 "  ::grpc::Service::MarkMethodAsync($Idx$);\n"
                 "}\n");
  printer->Print(*vars,
                 "~WithAsyncMethod_$Method$() override {\n"
                 "  BaseClassMustBeDerivedFromService(this);\n"
                 "}\n");
  if (method->NoStreaming()) {
    printer->Print(
        *vars,
        "// disable synchronous version of this method\n"
        "::grpc::Status $Method$("
        "::grpc::ServerContext* context, const $Request$* request, "
        "$Response$* response) final override {\n"
        "  abort();\n"
        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
    printer->Print(
        *vars,
        "void Request$Method$("
        "::grpc::ServerContext* context, $Request$* request, "
        "::grpc::ServerAsyncResponseWriter< $Response$>* response, "
        "::grpc::CompletionQueue* new_call_cq, "
        "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n");
    printer->Print(*vars,
                   "  ::grpc::Service::RequestAsyncUnary($Idx$, context, "
                   "request, response, new_call_cq, notification_cq, tag);\n");
    printer->Print("}\n");
  } else if (ClientOnlyStreaming(method)) {
    printer->Print(
        *vars,
        "// disable synchronous version of this method\n"
        "::grpc::Status $Method$("
        "::grpc::ServerContext* context, "
        "::grpc::ServerReader< $Request$>* reader, "
        "$Response$* response) final override {\n"
        "  abort();\n"
        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
    printer->Print(
        *vars,
        "void Request$Method$("
        "::grpc::ServerContext* context, "
        "::grpc::ServerAsyncReader< $Response$, $Request$>* reader, "
        "::grpc::CompletionQueue* new_call_cq, "
        "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n");
    printer->Print(*vars,
                   "  ::grpc::Service::RequestAsyncClientStreaming($Idx$, "
                   "context, reader, new_call_cq, notification_cq, tag);\n");
    printer->Print("}\n");
  } else if (ServerOnlyStreaming(method)) {
    printer->Print(
        *vars,
        "// disable synchronous version of this method\n"
        "::grpc::Status $Method$("
        "::grpc::ServerContext* context, const $Request$* request, "
        "::grpc::ServerWriter< $Response$>* writer) final override "
        "{\n"
        "  abort();\n"
        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
    printer->Print(
        *vars,
        "void Request$Method$("
        "::grpc::ServerContext* context, $Request$* request, "
        "::grpc::ServerAsyncWriter< $Response$>* writer, "
        "::grpc::CompletionQueue* new_call_cq, "
        "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n");
    printer->Print(
        *vars,
        "  ::grpc::Service::RequestAsyncServerStreaming($Idx$, "
        "context, request, writer, new_call_cq, notification_cq, tag);\n");
    printer->Print("}\n");
  } else if (method->BidiStreaming()) {
    printer->Print(
        *vars,
        "// disable synchronous version of this method\n"
        "::grpc::Status $Method$("
        "::grpc::ServerContext* context, "
        "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) "
        "final override {\n"
        "  abort();\n"
        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
    printer->Print(
        *vars,
        "void Request$Method$("
        "::grpc::ServerContext* context, "
        "::grpc::ServerAsyncReaderWriter< $Response$, $Request$>* stream, "
        "::grpc::CompletionQueue* new_call_cq, "
        "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n");
    printer->Print(*vars,
                   "  ::grpc::Service::RequestAsyncBidiStreaming($Idx$, "
                   "context, stream, new_call_cq, notification_cq, tag);\n");
    printer->Print("}\n");
  }
  printer->Outdent();
  printer->Print(*vars, "};\n");
}

void PrintHeaderServerMethodStreamedUnary(
    grpc_generator::Printer *printer, const grpc_generator::Method *method,
    std::map<grpc::string, grpc::string> *vars) {
  (*vars)["Method"] = method->name();
  (*vars)["Request"] = method->input_type_name();
  (*vars)["Response"] = method->output_type_name();
  if (method->NoStreaming()) {
    printer->Print(*vars, "template <class BaseClass>\n");
    printer->Print(*vars,
                   "class WithStreamedUnaryMethod_$Method$ : "
                   "public BaseClass {\n");
    printer->Print(
        " private:\n"
        "  void BaseClassMustBeDerivedFromService(const Service *service) "
        "{}\n");
    printer->Print(" public:\n");
    printer->Indent();
    printer->Print(*vars,
                   "WithStreamedUnaryMethod_$Method$() {\n"
                   "  ::grpc::Service::MarkMethodStreamed($Idx$,\n"
                   "    new ::grpc::StreamedUnaryHandler< $Request$, "
                   "$Response$>(std::bind"
                   "(&WithStreamedUnaryMethod_$Method$<BaseClass>::"
                   "Streamed$Method$, this, std::placeholders::_1, "
                   "std::placeholders::_2)));\n"
                   "}\n");
    printer->Print(*vars,
                   "~WithStreamedUnaryMethod_$Method$() override {\n"
                   "  BaseClassMustBeDerivedFromService(this);\n"
                   "}\n");
    printer->Print(
        *vars,
        "// disable regular version of this method\n"
        "::grpc::Status $Method$("
        "::grpc::ServerContext* context, const $Request$* request, "
        "$Response$* response) final override {\n"
        "  abort();\n"
        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
    printer->Print(*vars,
                   "// replace default version of method with streamed unary\n"
                   "virtual ::grpc::Status Streamed$Method$("
                   "::grpc::ServerContext* context, "
                   "::grpc::ServerUnaryStreamer< "
                   "$Request$,$Response$>* server_unary_streamer)"
                   " = 0;\n");
    printer->Outdent();
    printer->Print(*vars, "};\n");
  }
}

void PrintHeaderServerMethodSplitStreaming(
    grpc_generator::Printer *printer, const grpc_generator::Method *method,
    std::map<grpc::string, grpc::string> *vars) {
  (*vars)["Method"] = method->name();
  (*vars)["Request"] = method->input_type_name();
  (*vars)["Response"] = method->output_type_name();
  if (ServerOnlyStreaming(method)) {
    printer->Print(*vars, "template <class BaseClass>\n");
    printer->Print(*vars,
                   "class WithSplitStreamingMethod_$Method$ : "
                   "public BaseClass {\n");
    printer->Print(
        " private:\n"
        "  void BaseClassMustBeDerivedFromService(const Service *service) "
        "{}\n");
    printer->Print(" public:\n");
    printer->Indent();
    printer->Print(*vars,
                   "WithSplitStreamingMethod_$Method$() {\n"
                   "  ::grpc::Service::MarkMethodStreamed($Idx$,\n"
                   "    new ::grpc::SplitServerStreamingHandler< $Request$, "
                   "$Response$>(std::bind"
                   "(&WithSplitStreamingMethod_$Method$<BaseClass>::"
                   "Streamed$Method$, this, std::placeholders::_1, "
                   "std::placeholders::_2)));\n"
                   "}\n");
    printer->Print(*vars,
                   "~WithSplitStreamingMethod_$Method$() override {\n"
                   "  BaseClassMustBeDerivedFromService(this);\n"
                   "}\n");
    printer->Print(
        *vars,
        "// disable regular version of this method\n"
        "::grpc::Status $Method$("
        "::grpc::ServerContext* context, const $Request$* request, "
        "::grpc::ServerWriter< $Response$>* writer) final override "
        "{\n"
        "  abort();\n"
        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
    printer->Print(*vars,
                   "// replace default version of method with split streamed\n"
                   "virtual ::grpc::Status Streamed$Method$("
                   "::grpc::ServerContext* context, "
                   "::grpc::ServerSplitStreamer< "
                   "$Request$,$Response$>* server_split_streamer)"
                   " = 0;\n");
    printer->Outdent();
    printer->Print(*vars, "};\n");
  }
}

void PrintHeaderServerMethodGeneric(
    grpc_generator::Printer *printer, const grpc_generator::Method *method,
    std::map<grpc::string, grpc::string> *vars) {
  (*vars)["Method"] = method->name();
  (*vars)["Request"] = method->input_type_name();
  (*vars)["Response"] = method->output_type_name();
  printer->Print(*vars, "template <class BaseClass>\n");
  printer->Print(*vars,
                 "class WithGenericMethod_$Method$ : public BaseClass {\n");
  printer->Print(
      " private:\n"
      "  void BaseClassMustBeDerivedFromService(const Service *service) {}\n");
  printer->Print(" public:\n");
  printer->Indent();
  printer->Print(*vars,
                 "WithGenericMethod_$Method$() {\n"
                 "  ::grpc::Service::MarkMethodGeneric($Idx$);\n"
                 "}\n");
  printer->Print(*vars,
                 "~WithGenericMethod_$Method$() override {\n"
                 "  BaseClassMustBeDerivedFromService(this);\n"
                 "}\n");
  if (method->NoStreaming()) {
    printer->Print(
        *vars,
        "// disable synchronous version of this method\n"
        "::grpc::Status $Method$("
        "::grpc::ServerContext* context, const $Request$* request, "
        "$Response$* response) final override {\n"
        "  abort();\n"
        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
  } else if (ClientOnlyStreaming(method)) {
    printer->Print(
        *vars,
        "// disable synchronous version of this method\n"
        "::grpc::Status $Method$("
        "::grpc::ServerContext* context, "
        "::grpc::ServerReader< $Request$>* reader, "
        "$Response$* response) final override {\n"
        "  abort();\n"
        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
  } else if (ServerOnlyStreaming(method)) {
    printer->Print(
        *vars,
        "// disable synchronous version of this method\n"
        "::grpc::Status $Method$("
        "::grpc::ServerContext* context, const $Request$* request, "
        "::grpc::ServerWriter< $Response$>* writer) final override "
        "{\n"
        "  abort();\n"
        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
  } else if (method->BidiStreaming()) {
    printer->Print(
        *vars,
        "// disable synchronous version of this method\n"
        "::grpc::Status $Method$("
        "::grpc::ServerContext* context, "
        "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) "
        "final override {\n"
        "  abort();\n"
        "  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"
        "}\n");
  }
  printer->Outdent();
  printer->Print(*vars, "};\n");
}

void PrintHeaderService(grpc_generator::Printer *printer,
                        const grpc_generator::Service *service,
                        std::map<grpc::string, grpc::string> *vars) {
  (*vars)["Service"] = service->name();

  printer->Print(service->GetLeadingComments("//").c_str());
  printer->Print(*vars,
                 "class $Service$ final {\n"
                 " public:\n");
  printer->Indent();

  // Service metadata
  printer->Print(*vars,
                 "static constexpr char const* service_full_name() {\n"
                 "  return \"$Package$$Service$\";\n"
                 "}\n");

  // Client side
  printer->Print(
      "class StubInterface {\n"
      " public:\n");
  printer->Indent();
  printer->Print("virtual ~StubInterface() {}\n");
  for (int i = 0; i < service->method_count(); ++i) {
    printer->Print(service->method(i)->GetLeadingComments("//").c_str());
    PrintHeaderClientMethodInterfaces(printer, service->method(i).get(), vars,
                                      true);
    printer->Print(service->method(i)->GetTrailingComments("//").c_str());
  }
  printer->Outdent();
  printer->Print("private:\n");
  printer->Indent();
  for (int i = 0; i < service->method_count(); ++i) {
    PrintHeaderClientMethodInterfaces(printer, service->method(i).get(), vars,
                                      false);
  }
  printer->Outdent();
  printer->Print("};\n");
  printer->Print(
      "class Stub final : public StubInterface"
      " {\n public:\n");
  printer->Indent();
  printer->Print(
      "Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel);\n");
  for (int i = 0; i < service->method_count(); ++i) {
    PrintHeaderClientMethod(printer, service->method(i).get(), vars, true);
  }
  printer->Outdent();
  printer->Print("\n private:\n");
  printer->Indent();
  printer->Print("std::shared_ptr< ::grpc::ChannelInterface> channel_;\n");
  for (int i = 0; i < service->method_count(); ++i) {
    PrintHeaderClientMethod(printer, service->method(i).get(), vars, false);
  }
  for (int i = 0; i < service->method_count(); ++i) {
    PrintHeaderClientMethodData(printer, service->method(i).get(), vars);
  }
  printer->Outdent();
  printer->Print("};\n");
  printer->Print(
      "static std::unique_ptr<Stub> NewStub(const std::shared_ptr< "
      "::grpc::ChannelInterface>& channel, "
      "const ::grpc::StubOptions& options = ::grpc::StubOptions());\n");

  printer->Print("\n");

  // Server side - base
  printer->Print(
      "class Service : public ::grpc::Service {\n"
      " public:\n");
  printer->Indent();
  printer->Print("Service();\n");
  printer->Print("virtual ~Service();\n");
  for (int i = 0; i < service->method_count(); ++i) {
    PrintHeaderServerMethodSync(printer, service->method(i).get(), vars);
  }
  printer->Outdent();
  printer->Print("};\n");

  // Server side - Asynchronous
  for (int i = 0; i < service->method_count(); ++i) {
    (*vars)["Idx"] = as_string(i);
    PrintHeaderServerMethodAsync(printer, service->method(i).get(), vars);
  }

  printer->Print("typedef ");

  for (int i = 0; i < service->method_count(); ++i) {
    (*vars)["method_name"] = service->method(i).get()->name();
    printer->Print(*vars, "WithAsyncMethod_$method_name$<");
  }
  printer->Print("Service");
  for (int i = 0; i < service->method_count(); ++i) {
    printer->Print(" >");
  }
  printer->Print(" AsyncService;\n");

  // Server side - Generic
  for (int i = 0; i < service->method_count(); ++i) {
    (*vars)["Idx"] = as_string(i);
    PrintHeaderServerMethodGeneric(printer, service->method(i).get(), vars);
  }

  // Server side - Streamed Unary
  for (int i = 0; i < service->method_count(); ++i) {
    (*vars)["Idx"] = as_string(i);
    PrintHeaderServerMethodStreamedUnary(printer, service->method(i).get(),
                                         vars);
  }

  printer->Print("typedef ");
  for (int i = 0; i < service->method_count(); ++i) {
    (*vars)["method_name"] = service->method(i).get()->name();
    if (service->method(i)->NoStreaming()) {
      printer->Print(*vars, "WithStreamedUnaryMethod_$method_name$<");
    }
  }
  printer->Print("Service");
  for (int i = 0; i < service->method_count(); ++i) {
    if (service->method(i)->NoStreaming()) {
      printer->Print(" >");
    }
  }
  printer->Print(" StreamedUnaryService;\n");

  // Server side - controlled server-side streaming
  for (int i = 0; i < service->method_count(); ++i) {
    (*vars)["Idx"] = as_string(i);
    PrintHeaderServerMethodSplitStreaming(printer, service->method(i).get(),
                                          vars);
  }

  printer->Print("typedef ");
  for (int i = 0; i < service->method_count(); ++i) {
    (*vars)["method_name"] = service->method(i).get()->name();
    auto method = service->method(i);
    if (ServerOnlyStreaming(method.get())) {
      printer->Print(*vars, "WithSplitStreamingMethod_$method_name$<");
    }
  }
  printer->Print("Service");
  for (int i = 0; i < service->method_count(); ++i) {
    auto method = service->method(i);
    if (ServerOnlyStreaming(method.get())) {
      printer->Print(" >");
    }
  }
  printer->Print(" SplitStreamedService;\n");

  // Server side - typedef for controlled both unary and server-side streaming
  printer->Print("typedef ");
  for (int i = 0; i < service->method_count(); ++i) {
    (*vars)["method_name"] = service->method(i).get()->name();
    auto method = service->method(i);
    if (ServerOnlyStreaming(method.get())) {
      printer->Print(*vars, "WithSplitStreamingMethod_$method_name$<");
    }
    if (service->method(i)->NoStreaming()) {
      printer->Print(*vars, "WithStreamedUnaryMethod_$method_name$<");
    }
  }
  printer->Print("Service");
  for (int i = 0; i < service->method_count(); ++i) {
    auto method = service->method(i);
    if (service->method(i)->NoStreaming() ||
        ServerOnlyStreaming(method.get())) {
      printer->Print(" >");
    }
  }
  printer->Print(" StreamedService;\n");

  printer->Outdent();
  printer->Print("};\n");
  printer->Print(service->GetTrailingComments("//").c_str());
}

grpc::string GetHeaderServices(grpc_generator::File *file,
                               const Parameters &params) {
  grpc::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(&output);
    std::map<grpc::string, grpc::string> vars;
    // Package string is empty or ends with a dot. It is used to fully qualify
    // method names.
    vars["Package"] = file->package();
    if (!file->package().empty()) {
      vars["Package"].append(".");
    }

    if (!params.services_namespace.empty()) {
      vars["services_namespace"] = params.services_namespace;
      printer->Print(vars, "\nnamespace $services_namespace$ {\n\n");
    }

    for (int i = 0; i < file->service_count(); ++i) {
      PrintHeaderService(printer.get(), file->service(i).get(), &vars);
      printer->Print("\n");
    }

    if (!params.services_namespace.empty()) {
      printer->Print(vars, "}  // namespace $services_namespace$\n\n");
    }
  }
  return output;
}

grpc::string GetHeaderEpilogue(grpc_generator::File *file,
                               const Parameters & /*params*/) {
  grpc::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(&output);
    std::map<grpc::string, grpc::string> vars;

    vars["filename"] = file->filename();
    vars["filename_identifier"] = FilenameIdentifier(file->filename());

    if (!file->package().empty()) {
      std::vector<grpc::string> parts = file->package_parts();

      for (auto part = parts.rbegin(); part != parts.rend(); part++) {
        vars["part"] = *part;
        printer->Print(vars, "}  // namespace $part$\n");
      }
      printer->Print(vars, "\n");
    }

    printer->Print(vars, "\n");
    printer->Print(vars, "#endif  // GRPC_$filename_identifier$__INCLUDED\n");

    printer->Print(file->GetTrailingComments("//").c_str());
  }
  return output;
}

grpc::string GetSourcePrologue(grpc_generator::File *file,
                               const Parameters & /*params*/) {
  grpc::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(&output);
    std::map<grpc::string, grpc::string> vars;

    vars["filename"] = file->filename();
    vars["filename_base"] = file->filename_without_ext();
    vars["message_header_ext"] = kCppGeneratorMessageHeaderExt;
    vars["service_header_ext"] = kCppGeneratorServiceHeaderExt;

    printer->Print(vars, "// Generated by the gRPC C++ plugin.\n");
    printer->Print(vars,
                   "// If you make any local change, they will be lost.\n");
    printer->Print(vars, "// source: $filename$\n\n");

    printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n");
    printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n");
    printer->Print(vars, "\n");
  }
  return output;
}

grpc::string GetSourceIncludes(grpc_generator::File *file,
                               const Parameters &params) {
  grpc::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(&output);
    std::map<grpc::string, grpc::string> vars;

    static const char *headers_strs[] = {
        "grpc++/impl/codegen/async_stream.h",
        "grpc++/impl/codegen/async_unary_call.h",
        "grpc++/impl/codegen/channel_interface.h",
        "grpc++/impl/codegen/client_unary_call.h",
        "grpc++/impl/codegen/method_handler_impl.h",
        "grpc++/impl/codegen/rpc_service_method.h",
        "grpc++/impl/codegen/service_type.h",
        "grpc++/impl/codegen/sync_stream.h"};
    std::vector<grpc::string> headers(headers_strs, array_end(headers_strs));
    PrintIncludes(printer.get(), headers, params);

    if (!file->package().empty()) {
      std::vector<grpc::string> parts = file->package_parts();

      for (auto part = parts.begin(); part != parts.end(); part++) {
        vars["part"] = *part;
        printer->Print(vars, "namespace $part$ {\n");
      }
    }

    printer->Print(vars, "\n");
  }
  return output;
}

void PrintSourceClientMethod(grpc_generator::Printer *printer,
                             const grpc_generator::Method *method,
                             std::map<grpc::string, grpc::string> *vars) {
  (*vars)["Method"] = method->name();
  (*vars)["Request"] = method->input_type_name();
  (*vars)["Response"] = method->output_type_name();
  if (method->NoStreaming()) {
    printer->Print(*vars,
                   "::grpc::Status $ns$$Service$::Stub::$Method$("
                   "::grpc::ClientContext* context, "
                   "const $Request$& request, $Response$* response) {\n");
    printer->Print(*vars,
                   "  return ::grpc::BlockingUnaryCall(channel_.get(), "
                   "rpcmethod_$Method$_, "
                   "context, request, response);\n"
                   "}\n\n");
    printer->Print(
        *vars,
        "::grpc::ClientAsyncResponseReader< $Response$>* "
        "$ns$$Service$::Stub::Async$Method$Raw(::grpc::ClientContext* context, "
        "const $Request$& request, "
        "::grpc::CompletionQueue* cq) {\n");
    printer->Print(*vars,
                   "  return "
                   "::grpc::ClientAsyncResponseReader< $Response$>::Create("
                   "channel_.get(), cq, "
                   "rpcmethod_$Method$_, "
                   "context, request);\n"
                   "}\n\n");
  } else if (ClientOnlyStreaming(method)) {
    printer->Print(*vars,
                   "::grpc::ClientWriter< $Request$>* "
                   "$ns$$Service$::Stub::$Method$Raw("
                   "::grpc::ClientContext* context, $Response$* response) {\n");
    printer->Print(*vars,
                   "  return new ::grpc::ClientWriter< $Request$>("
                   "channel_.get(), "
                   "rpcmethod_$Method$_, "
                   "context, response);\n"
                   "}\n\n");
    printer->Print(*vars,
                   "::grpc::ClientAsyncWriter< $Request$>* "
                   "$ns$$Service$::Stub::Async$Method$Raw("
                   "::grpc::ClientContext* context, $Response$* response, "
                   "::grpc::CompletionQueue* cq, void* tag) {\n");
    printer->Print(*vars,
                   "  return ::grpc::ClientAsyncWriter< $Request$>::Create("
                   "channel_.get(), cq, "
                   "rpcmethod_$Method$_, "
                   "context, response, tag);\n"
                   "}\n\n");
  } else if (ServerOnlyStreaming(method)) {
    printer->Print(
        *vars,
        "::grpc::ClientReader< $Response$>* "
        "$ns$$Service$::Stub::$Method$Raw("
        "::grpc::ClientContext* context, const $Request$& request) {\n");
    printer->Print(*vars,
                   "  return new ::grpc::ClientReader< $Response$>("
                   "channel_.get(), "
                   "rpcmethod_$Method$_, "
                   "context, request);\n"
                   "}\n\n");
    printer->Print(*vars,
                   "::grpc::ClientAsyncReader< $Response$>* "
                   "$ns$$Service$::Stub::Async$Method$Raw("
                   "::grpc::ClientContext* context, const $Request$& request, "
                   "::grpc::CompletionQueue* cq, void* tag) {\n");
    printer->Print(*vars,
                   "  return ::grpc::ClientAsyncReader< $Response$>::Create("
                   "channel_.get(), cq, "
                   "rpcmethod_$Method$_, "
                   "context, request, tag);\n"
                   "}\n\n");
  } else if (method->BidiStreaming()) {
    printer->Print(
        *vars,
        "::grpc::ClientReaderWriter< $Request$, $Response$>* "
        "$ns$$Service$::Stub::$Method$Raw(::grpc::ClientContext* context) {\n");
    printer->Print(*vars,
                   "  return new ::grpc::ClientReaderWriter< "
                   "$Request$, $Response$>("
                   "channel_.get(), "
                   "rpcmethod_$Method$_, "
                   "context);\n"
                   "}\n\n");
    printer->Print(
        *vars,
        "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>* "
        "$ns$$Service$::Stub::Async$Method$Raw(::grpc::ClientContext* context, "
        "::grpc::CompletionQueue* cq, void* tag) {\n");
    printer->Print(
        *vars,
        "  return "
        "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>::Create("
        "channel_.get(), cq, "
        "rpcmethod_$Method$_, "
        "context, tag);\n"
        "}\n\n");
  }
}
void PrintSourceServerMethod(grpc_generator::Printer *printer,
                             const grpc_generator::Method *method,
                             std::map<grpc::string, grpc::string> *vars) {
  (*vars)["Method"] = method->name();
  (*vars)["Request"] = method->input_type_name();
  (*vars)["Response"] = method->output_type_name();
  if (method->NoStreaming()) {
    printer->Print(*vars,
                   "::grpc::Status $ns$$Service$::Service::$Method$("
                   "::grpc::ServerContext* context, "
                   "const $Request$* request, $Response$* response) {\n");
    printer->Print("  (void) context;\n");
    printer->Print("  (void) request;\n");
    printer->Print("  (void) response;\n");
    printer->Print(
        "  return ::grpc::Status("
        "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
    printer->Print("}\n\n");
  } else if (ClientOnlyStreaming(method)) {
    printer->Print(*vars,
                   "::grpc::Status $ns$$Service$::Service::$Method$("
                   "::grpc::ServerContext* context, "
                   "::grpc::ServerReader< $Request$>* reader, "
                   "$Response$* response) {\n");
    printer->Print("  (void) context;\n");
    printer->Print("  (void) reader;\n");
    printer->Print("  (void) response;\n");
    printer->Print(
        "  return ::grpc::Status("
        "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
    printer->Print("}\n\n");
  } else if (ServerOnlyStreaming(method)) {
    printer->Print(*vars,
                   "::grpc::Status $ns$$Service$::Service::$Method$("
                   "::grpc::ServerContext* context, "
                   "const $Request$* request, "
                   "::grpc::ServerWriter< $Response$>* writer) {\n");
    printer->Print("  (void) context;\n");
    printer->Print("  (void) request;\n");
    printer->Print("  (void) writer;\n");
    printer->Print(
        "  return ::grpc::Status("
        "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
    printer->Print("}\n\n");
  } else if (method->BidiStreaming()) {
    printer->Print(*vars,
                   "::grpc::Status $ns$$Service$::Service::$Method$("
                   "::grpc::ServerContext* context, "
                   "::grpc::ServerReaderWriter< $Response$, $Request$>* "
                   "stream) {\n");
    printer->Print("  (void) context;\n");
    printer->Print("  (void) stream;\n");
    printer->Print(
        "  return ::grpc::Status("
        "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
    printer->Print("}\n\n");
  }
}

void PrintSourceService(grpc_generator::Printer *printer,
                        const grpc_generator::Service *service,
                        std::map<grpc::string, grpc::string> *vars) {
  (*vars)["Service"] = service->name();

  if (service->method_count() > 0) {
    printer->Print(*vars,
                   "static const char* $prefix$$Service$_method_names[] = {\n");
    for (int i = 0; i < service->method_count(); ++i) {
      (*vars)["Method"] = service->method(i).get()->name();
      printer->Print(*vars, "  \"/$Package$$Service$/$Method$\",\n");
    }
    printer->Print(*vars, "};\n\n");
  }

  printer->Print(*vars,
                 "std::unique_ptr< $ns$$Service$::Stub> $ns$$Service$::NewStub("
                 "const std::shared_ptr< ::grpc::ChannelInterface>& channel, "
                 "const ::grpc::StubOptions& options) {\n"
                 "  std::unique_ptr< $ns$$Service$::Stub> stub(new "
                 "$ns$$Service$::Stub(channel));\n"
                 "  return stub;\n"
                 "}\n\n");
  printer->Print(*vars,
                 "$ns$$Service$::Stub::Stub(const std::shared_ptr< "
                 "::grpc::ChannelInterface>& channel)\n");
  printer->Indent();
  printer->Print(": channel_(channel)");
  for (int i = 0; i < service->method_count(); ++i) {
    auto method = service->method(i);
    (*vars)["Method"] = method->name();
    (*vars)["Idx"] = as_string(i);
    if (method->NoStreaming()) {
      (*vars)["StreamingType"] = "NORMAL_RPC";
      // NOTE: There is no reason to consider streamed-unary as a separate
      // category here since this part is setting up the client-side stub
      // and this appears as a NORMAL_RPC from the client-side.
    } else if (ClientOnlyStreaming(method.get())) {
      (*vars)["StreamingType"] = "CLIENT_STREAMING";
    } else if (ServerOnlyStreaming(method.get())) {
      (*vars)["StreamingType"] = "SERVER_STREAMING";
    } else {
      (*vars)["StreamingType"] = "BIDI_STREAMING";
    }
    printer->Print(*vars,
                   ", rpcmethod_$Method$_("
                   "$prefix$$Service$_method_names[$Idx$], "
                   "::grpc::RpcMethod::$StreamingType$, "
                   "channel"
                   ")\n");
  }
  printer->Print("{}\n\n");
  printer->Outdent();

  for (int i = 0; i < service->method_count(); ++i) {
    (*vars)["Idx"] = as_string(i);
    PrintSourceClientMethod(printer, service->method(i).get(), vars);
  }

  printer->Print(*vars, "$ns$$Service$::Service::Service() {\n");
  printer->Indent();
  for (int i = 0; i < service->method_count(); ++i) {
    auto method = service->method(i);
    (*vars)["Idx"] = as_string(i);
    (*vars)["Method"] = method->name();
    (*vars)["Request"] = method->input_type_name();
    (*vars)["Response"] = method->output_type_name();
    if (method->NoStreaming()) {
      printer->Print(
          *vars,
          "AddMethod(new ::grpc::RpcServiceMethod(\n"
          "    $prefix$$Service$_method_names[$Idx$],\n"
          "    ::grpc::RpcMethod::NORMAL_RPC,\n"
          "    new ::grpc::RpcMethodHandler< $ns$$Service$::Service, "
          "$Request$, "
          "$Response$>(\n"
          "        std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n");
    } else if (ClientOnlyStreaming(method.get())) {
      printer->Print(
          *vars,
          "AddMethod(new ::grpc::RpcServiceMethod(\n"
          "    $prefix$$Service$_method_names[$Idx$],\n"
          "    ::grpc::RpcMethod::CLIENT_STREAMING,\n"
          "    new ::grpc::ClientStreamingHandler< "
          "$ns$$Service$::Service, $Request$, $Response$>(\n"
          "        std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n");
    } else if (ServerOnlyStreaming(method.get())) {
      printer->Print(
          *vars,
          "AddMethod(new ::grpc::RpcServiceMethod(\n"
          "    $prefix$$Service$_method_names[$Idx$],\n"
          "    ::grpc::RpcMethod::SERVER_STREAMING,\n"
          "    new ::grpc::ServerStreamingHandler< "
          "$ns$$Service$::Service, $Request$, $Response$>(\n"
          "        std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n");
    } else if (method->BidiStreaming()) {
      printer->Print(
          *vars,
          "AddMethod(new ::grpc::RpcServiceMethod(\n"
          "    $prefix$$Service$_method_names[$Idx$],\n"
          "    ::grpc::RpcMethod::BIDI_STREAMING,\n"
          "    new ::grpc::BidiStreamingHandler< "
          "$ns$$Service$::Service, $Request$, $Response$>(\n"
          "        std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n");
    }
  }
  printer->Outdent();
  printer->Print(*vars, "}\n\n");
  printer->Print(*vars,
                 "$ns$$Service$::Service::~Service() {\n"
                 "}\n\n");
  for (int i = 0; i < service->method_count(); ++i) {
    (*vars)["Idx"] = as_string(i);
    PrintSourceServerMethod(printer, service->method(i).get(), vars);
  }
}

grpc::string GetSourceServices(grpc_generator::File *file,
                               const Parameters &params) {
  grpc::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(&output);
    std::map<grpc::string, grpc::string> vars;
    // Package string is empty or ends with a dot. It is used to fully qualify
    // method names.
    vars["Package"] = file->package();
    if (!file->package().empty()) {
      vars["Package"].append(".");
    }
    if (!params.services_namespace.empty()) {
      vars["ns"] = params.services_namespace + "::";
      vars["prefix"] = params.services_namespace;
    } else {
      vars["ns"] = "";
      vars["prefix"] = "";
    }

    for (int i = 0; i < file->service_count(); ++i) {
      PrintSourceService(printer.get(), file->service(i).get(), &vars);
      printer->Print("\n");
    }
  }
  return output;
}

grpc::string GetSourceEpilogue(grpc_generator::File *file,
                               const Parameters & /*params*/) {
  grpc::string temp;

  if (!file->package().empty()) {
    std::vector<grpc::string> parts = file->package_parts();

    for (auto part = parts.begin(); part != parts.end(); part++) {
      temp.append("}  // namespace ");
      temp.append(*part);
      temp.append("\n");
    }
    temp.append("\n");
  }

  return temp;
}

// TODO(mmukhi): Make sure we need parameters or not.
grpc::string GetMockPrologue(grpc_generator::File *file,
                             const Parameters & /*params*/) {
  grpc::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(&output);
    std::map<grpc::string, grpc::string> vars;

    vars["filename"] = file->filename();
    vars["filename_base"] = file->filename_without_ext();
    vars["message_header_ext"] = kCppGeneratorMessageHeaderExt;
    vars["service_header_ext"] = kCppGeneratorServiceHeaderExt;

    printer->Print(vars, "// Generated by the gRPC C++ plugin.\n");
    printer->Print(vars,
                   "// If you make any local change, they will be lost.\n");
    printer->Print(vars, "// source: $filename$\n\n");

    printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n");
    printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n");
    printer->Print(vars, file->additional_headers().c_str());
    printer->Print(vars, "\n");
  }
  return output;
}

// TODO(mmukhi): Add client-stream and completion-queue headers.
grpc::string GetMockIncludes(grpc_generator::File *file,
                             const Parameters &params) {
  grpc::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(&output);
    std::map<grpc::string, grpc::string> vars;

    static const char *headers_strs[] = {
        "grpc++/impl/codegen/async_stream.h",
        "grpc++/impl/codegen/sync_stream.h", "gmock/gmock.h",
    };
    std::vector<grpc::string> headers(headers_strs, array_end(headers_strs));
    PrintIncludes(printer.get(), headers, params);

    if (!file->package().empty()) {
      std::vector<grpc::string> parts = file->package_parts();

      for (auto part = parts.begin(); part != parts.end(); part++) {
        vars["part"] = *part;
        printer->Print(vars, "namespace $part$ {\n");
      }
    }

    printer->Print(vars, "\n");
  }
  return output;
}
void PrintMockClientMethods(grpc_generator::Printer *printer,
                            const grpc_generator::Method *method,
                            std::map<grpc::string, grpc::string> *vars) {
  (*vars)["Method"] = method->name();
  (*vars)["Request"] = method->input_type_name();
  (*vars)["Response"] = method->output_type_name();

  if (method->NoStreaming()) {
    printer->Print(
        *vars,
        "MOCK_METHOD3($Method$, ::grpc::Status(::grpc::ClientContext* context, "
        "const $Request$& request, $Response$* response));\n");
    printer->Print(*vars,
                   "MOCK_METHOD3(Async$Method$Raw, "
                   "::grpc::ClientAsyncResponseReaderInterface< $Response$>*"
                   "(::grpc::ClientContext* context, const $Request$& request, "
                   "::grpc::CompletionQueue* cq));\n");
  } else if (ClientOnlyStreaming(method)) {
    printer->Print(
        *vars,
        "MOCK_METHOD2($Method$Raw, "
        "::grpc::ClientWriterInterface< $Request$>*"
        "(::grpc::ClientContext* context, $Response$* response));\n");
    printer->Print(*vars,
                   "MOCK_METHOD4(Async$Method$Raw, "
                   "::grpc::ClientAsyncWriterInterface< $Request$>*"
                   "(::grpc::ClientContext* context, $Response$* response, "
                   "::grpc::CompletionQueue* cq, void* tag));\n");
  } else if (ServerOnlyStreaming(method)) {
    printer->Print(
        *vars,
        "MOCK_METHOD2($Method$Raw, "
        "::grpc::ClientReaderInterface< $Response$>*"
        "(::grpc::ClientContext* context, const $Request$& request));\n");
    printer->Print(*vars,
                   "MOCK_METHOD4(Async$Method$Raw, "
                   "::grpc::ClientAsyncReaderInterface< $Response$>*"
                   "(::grpc::ClientContext* context, const $Request$& request, "
                   "::grpc::CompletionQueue* cq, void* tag));\n");
  } else if (method->BidiStreaming()) {
    printer->Print(
        *vars,
        "MOCK_METHOD1($Method$Raw, "
        "::grpc::ClientReaderWriterInterface< $Request$, $Response$>*"
        "(::grpc::ClientContext* context));\n");
    printer->Print(
        *vars,
        "MOCK_METHOD3(Async$Method$Raw, "
        "::grpc::ClientAsyncReaderWriterInterface<$Request$, $Response$>*"
        "(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq, "
        "void* tag));\n");
  }
}

void PrintMockService(grpc_generator::Printer *printer,
                      const grpc_generator::Service *service,
                      std::map<grpc::string, grpc::string> *vars) {
  (*vars)["Service"] = service->name();

  printer->Print(*vars,
                 "class Mock$Service$Stub : public $Service$::StubInterface {\n"
                 " public:\n");
  printer->Indent();
  for (int i = 0; i < service->method_count(); ++i) {
    PrintMockClientMethods(printer, service->method(i).get(), vars);
  }
  printer->Outdent();
  printer->Print("};\n");
}
grpc::string GetMockServices(grpc_generator::File *file,
                             const Parameters &params) {
  grpc::string output;
  {
    // Scope the output stream so it closes and finalizes output to the string.
    auto printer = file->CreatePrinter(&output);
    std::map<grpc::string, grpc::string> vars;
    // Package string is empty or ends with a dot. It is used to fully qualify
    // method names.
    vars["Package"] = file->package();
    if (!file->package().empty()) {
      vars["Package"].append(".");
    }

    if (!params.services_namespace.empty()) {
      vars["services_namespace"] = params.services_namespace;
      printer->Print(vars, "\nnamespace $services_namespace$ {\n\n");
    }

    for (int i = 0; i < file->service_count(); i++) {
      PrintMockService(printer.get(), file->service(i).get(), &vars);
      printer->Print("\n");
    }

    if (!params.services_namespace.empty()) {
      printer->Print(vars, "} // namespace $services_namespace$\n\n");
    }
  }
  return output;
}

grpc::string GetMockEpilogue(grpc_generator::File *file,
                             const Parameters & /*params*/) {
  grpc::string temp;

  if (!file->package().empty()) {
    std::vector<grpc::string> parts = file->package_parts();

    for (auto part = parts.begin(); part != parts.end(); part++) {
      temp.append("} // namespace ");
      temp.append(*part);
      temp.append("\n");
    }
    temp.append("\n");
  }

  return temp;
}

}  // namespace grpc_cpp_generator