#!/usr/local/bin/python2.6 -tt
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#

from cStringIO import StringIO
from itertools import chain, ifilter
from collections import namedtuple
import copy
import errno
import os
import re
import sys

from thrift_compiler import frontend
# Easy access to the enum of t_base_type::t_base
from thrift_compiler.frontend import t_base
# Easy access to the enum of t_field::e_req
from thrift_compiler.frontend import e_req
# Easy access to the enum of t_const_value::t_const_value_type
from thrift_compiler.frontend import e_const_value_type as e_cv_type

from thrift_compiler.generate import t_generator

from thrift_compiler.generate.t_cpp_context import *
from thrift_compiler.generate.t_output_aggregator import get_global_scope
from thrift_compiler.generate.t_output_aggregator import out
from thrift_compiler.generate.t_output import IndentedOutput

# TODO move from here
class OrderedDict(dict):
    def __init__(self, *a, **b):
        dict.__init__(self, *a, **b)
        self._order = []

    def __setitem__(self, index, item):
        dict.__setitem__(self, index, item)
        self._order.append(index)

    def iteritems(self):
        return ((key, self[key]) for key in self._order)

# Same as map.get, but works for almost-dictionary-like types
# (like the ones generated by boost.python)
def _map_get(map, key, default=None):
    return map[key] if key in map else default

def _lift_unit(typ):
    if typ == 'void':
        return 'folly::Unit'
    return typ

# ---------------------------------------------------------------
# Generator
# ---------------------------------------------------------------

class CompilerError(RuntimeError):
    pass

SerializedFieldOptions = namedtuple('SerializedFieldOptions',
        ['has_serialized_fields', 'keep_unknown_fields'])

class CppGenerator(t_generator.Generator):
    '''
    Plain ol' c++ generator
    Note: this is NOT legitimacy incarnate
    '''

    # Protocols to generate client/server code for.
    protocols = [("binary", "BinaryProtocol", "T_BINARY_PROTOCOL"),
                 ("compact", "CompactProtocol", "T_COMPACT_PROTOCOL")]
    short_name = 'cpp2'
    long_name = 'C++ version 2'
    supported_flags = {
        'include_prefix': 'Use full include paths in generated files.',
        'cob_style': '',
        'no_client_completion': '',
        'bootstrap': '',
        'compatibility': 'Use thrift1 structs instead of generating new ones',
        'terse_writes': 'Avoid emitting unspec fields whose values are default',
        'stack_arguments': 'Pass arguments on stack instead of heap',
        'process_in_event_base': 'Process request in event base thread',
        'frozen2': 'enable frozen structures',
        'json': 'enable simple json protocol',
        'implicit_templates' : 'templates are instantiated implicitly' +
                               'instead of explicitly',
        'separate_processmap': "generate processmap in separate files",
        'optionals': "produce folly::Optional<...> for optional members",
        'fatal': 'uses the Fatal library to generate reflection metadata',
    }
    _out_dir_base = 'gen-cpp2'
    _compatibility_dir_base = 'gen-cpp'

    _base_to_cpp_typename = {
        t_base.void: 'void',
        t_base.string: 'std::string',
        t_base.bool: 'bool',
        t_base.byte: 'int8_t',
        t_base.i16: 'int16_t',
        t_base.i32: 'int32_t',
        t_base.i64: 'int64_t',
        t_base.double: 'double',
        t_base.float: 'float',
    }

    _serialized_fields_name = '__serialized'
    _serialized_fields_type = 'apache::thrift::CloneableIOBuf'
    _serialized_fields_protocol_name = '__serialized_protocol'

    def __init__(self, *args, **kwargs):
        # super constructor
        t_generator.Generator.__init__(self, *args, **kwargs)
        prefix = self._flags.get('include_prefix')
        if isinstance(prefix, basestring):
            self.program.include_prefix = prefix
        terse_writes = self._flags.get('terse_writes')
        self.safe_terse_writes = (terse_writes == 'safe')
        if self.flag_json:
            self.protocols = copy.deepcopy(CppGenerator.protocols)
            self.protocols.append(
                ("simple_json", "SimpleJSONProtocol", "T_SIMPLE_JSON_PROTOCOL"))

    def _base_type_name(self, tbase):
        if tbase in self._base_to_cpp_typename:
            return self._base_to_cpp_typename[tbase]
        raise CompilerError('no C++ base type name for base type ' + tbase)

    def _cpp_annotation(self, type, key, default=None):
        t = _map_get(type.annotations, 'cpp2.' + key)
        if t:
            if self.flag_compatibility:
                raise CompilerError(
                    'cpp2.{0} annotation not allowed in compatibility mode: '
                    '{1}'.format(key, type))
            return t
        t = _map_get(type.annotations, 'cpp.' + key)
        if t:
            return t
        return default

    def _has_cpp_annotation(self, type, key):
        return self._cpp_annotation(type, key) is not None

    def _cpp_type_name(self, type, default=None):
        return self._cpp_annotation(type, 'type', default)

    def _type_name(self, ttype, in_typedef=False,
                   arg=False, scope=None, unique=False):
        unique = unique and not self.flag_stack_arguments
        if ttype.is_stream:
            ttype = ttype.as_stream.elem_type
        if ttype.is_base_type:
            # cast it
            btype = ttype.as_base_type
            bname = self._base_type_name(btype.base)
            if arg and ttype.is_string:
                return self._reference_name(bname, unique)
            return self._cpp_type_name(ttype, bname)
        # Check for a custom overloaded C++ name
        if ttype.is_container:
            tcontainer = ttype.as_container
            inner_types = None
            template = self._cpp_annotation(tcontainer, 'template')
            cname = self._cpp_type_name(tcontainer)
            if cname:
                pass
            elif ttype.is_map:
                tmap = ttype.as_map
                if template:
                    cname = template
                elif tmap.is_unordered:
                    cname = 'std::unordered_map'
                else:
                    cname = 'std::map'
                inner_types = [self._type_name(tmap.key_type, in_typedef,
                                               scope=scope),
                               self._type_name(tmap.value_type, in_typedef,
                                               scope=scope)]
                cname = cname + '<{0}, {1}>'
            elif ttype.is_set:
                tset = ttype.as_set
                if template:
                    cname = template
                elif tset.is_unordered:
                    cname = 'std::unordered_set'
                else:
                    cname = 'std::set'
                inner_types = [self._type_name(tset.elem_type, in_typedef,
                                               scope=scope)]
                cname = cname + "<{0}>"
            elif ttype.is_list:
                cname = 'std::vector'
                if template:
                    cname = template
                tlist = ttype.as_list
                inner_types = [self._type_name(tlist.elem_type, in_typedef,
                                               scope=scope)]
                cname = cname + "<{0}>"
            if inner_types:
                cname = cname.format(*inner_types)
            if arg:
                return self._reference_name(cname, unique)
            else:
                return cname

        if in_typedef and (ttype.is_struct or ttype.is_xception) and \
                ttype.program == self._program:
            if self.flag_compatibility:
                scope('typedef ' + self._namespace_prefix(
                        self._program.get_namespace('cpp')) + ttype.name + \
                          ' ' + ttype.name + ';')
            else:
                scope('class ' + ttype.name + ';')

        tname = self._cpp_type_name(ttype)
        if not tname:
            # Check if it needs to be namespaced
            program = ttype.program
            if program is not None:
                tname = self._namespace_prefix(self._get_namespace(program)) + \
                        ttype.name
            else:
                tname = ttype.name

        if arg and self._is_complex_type(ttype):
            return self._reference_name(tname, unique)
        else:
            return tname

    def _is_orderable_type(self, ttype):
        if ttype.is_base_type:
            return True
        elif ttype.is_enum:
            return True
        elif ttype.is_struct or ttype.is_xception:
            for m in ttype.as_struct.members:
                if m.req == e_req.optional \
                  or self._has_cpp_annotation(m.type, 'template') \
                  or not self._is_orderable_type(m.type):
                    return False
            return True
        elif ttype.is_map and not ttype.as_map.is_unordered:
            return self._is_orderable_type(ttype.as_map.key_type) \
                    and self._is_orderable_type(ttype.as_map.value_type)
        elif ttype.is_set and not ttype.as_set.is_unordered:
            return self._is_orderable_type(ttype.as_set.elem_type)
        elif ttype.is_list:
            return self._is_orderable_type(ttype.as_list.elem_type)
        else:
            return False

    def _get_cache_key(self, f):
        foundMember = None
        for member in f.arglist.members:
            if self._has_cpp_annotation(member, "cache"):
                if foundMember is not None:
                    raise CompilerError('Multiple cache \
                            annotations are not allowed')
                else:
                    foundMember = member
        return foundMember

    def _is_reference(self, f):
        return self._has_cpp_annotation(f, "ref")

    def _is_optional_wrapped(self, f):
        return f.req == e_req.optional and self.flag_optionals

    def _has_isset(self, f):
        return not self._is_reference(f) and f.req != e_req.required \
            and not self._is_optional_wrapped(f)

    # noncopyable is a hack to support gcc < 4.8, where declaring a constructor
    # as defaulted tries to generate it, even though it should be deleted.
    def _is_copyable_struct(self, ttype):
        assert ttype.is_struct or ttype.is_xception or ttype.is_union
        return not self._has_cpp_annotation(ttype, "noncopyable")

    def _is_comparable_struct(self, ttype):
        assert ttype.is_struct or ttype.is_xception or ttype.is_union
        return not self._has_cpp_annotation(ttype, "noncomparable")

    def _is_noex_move_ctor_struct(self, ttype):
        return ttype.is_struct and \
            self._has_cpp_annotation(ttype, "noexcept_move_ctor")

    def _reference_name(self, name, unique):
        if unique:
            return "std::unique_ptr<{0}>".format(name)
        else:
            return "const {0}&".format(name)

    def _get_namespace(self, program=None):
        if program == None:
            program = self._program
        ns = program.get_namespace('cpp2')
        if ns == '':
            if len(program.get_namespace('cpp')) > 0:
                ns = program.get_namespace('cpp') + '.cpp2'
            else:
                ns = 'cpp2'
        return ns

    def _namespace_prefix(self, ns):
        'Return the absolute c++ prefix for the .-separated namespace param'
        prefix = ' ::' + '::'.join(ns.split('.'))
        if len(ns) > 0:
            prefix += '::'
        return prefix

    def _is_complex_type(self, ttype):
        ttype = self._get_true_type(ttype)
        return ttype.is_container or \
               ttype.is_struct or \
               ttype.is_xception or \
               ttype.is_base_type and \
                    ttype.as_base_type.base == frontend.t_base.string

    def _get_true_type(self, ttype):
        'Get the true type behind a series of typedefs and streams'
        while ttype.is_typedef or ttype.is_stream:
            if ttype.is_typedef:
                ttype = ttype.as_typedef.type
            else:
                ttype = ttype.as_stream.elem_type
        return ttype

    def _is_stream_type(self, ttype):
        while ttype.is_typedef:
            ttype = ttype.as_typedef.type
        return ttype.is_stream

    def _type_access_suffix(self, ttype):
        if not ttype.is_typedef:
            return ''
        return self._cpp_annotation(ttype.as_typedef.type, 'indirection', '')

    def _gen_forward_declaration(self, tstruct):
        if not self.flag_compatibility:
            out("class {0};".format(tstruct.name))

    def _get_handler_callback_class(self, function):
        if self._is_complex_type(function.returntype) and \
                not self.flag_stack_arguments:
            rettype = self._type_name(function.returntype)
            rettype = 'std::unique_ptr<' + rettype + '>'
        else:
            rettype = self._type_name(function.returntype)
        cb = 'StreamingHandlerCallback' if self._is_stream_type(function.returntype) \
                else 'HandlerCallback'
        return 'apache::thrift::{0}<{1}>'.format(cb, rettype)

    def _get_presult_success(self):
        if self.flag_compatibility:
            return 'result.success'
        else:
            return 'result.get<0>().value'
    def _get_presult_success_isset(self, value_if_set=None):
        if self.flag_compatibility:
            if value_if_set is None:
                return 'result.__isset.success'
            else:
                return 'result.__isset.success = {0}'.format(value_if_set)
        else:
            if value_if_set is None:
                return 'result.getIsSet(0)'
            else:
                return 'result.setIsSet(0, {0})'.format(value_if_set)
    def _get_presult_exception(self, ex_idx, e):
        if self.flag_compatibility:
            return 'result.{0}'.format(e.name)
        else:
            return 'result.get<{0}>().ref()'.format(ex_idx)
    def _get_presult_exception_isset(self, ex_idx, e, value_if_set=None):
        if self.flag_compatibility:
            if value_if_set is None:
                return 'result.__isset.{0}'.format(e.name)
            else:
                return 'result.__isset.{0} = {1}'.format(e.name, value_if_set)
        else:
            if value_if_set is None:
                return 'result.getIsSet({0})'.format(ex_idx)
            else:
                return 'result.setIsSet({0}, {1})'.format(ex_idx, value_if_set)
    def _get_pargs_field(self, idx, f, pointer=True):
        if self.flag_compatibility:
            return '{0}args.{1}'.format('' if pointer else '*', f.name)
        else:
            return 'args.get<{0}>().{1}'.format(idx,
                    'value' if pointer else 'ref()')

    def _generate_enum_constant_list(self, enum, constants, quote_names,
                                      include_values):
        if include_values:
            name_format = '{0.name}'
            value_format = ' = {0.value}'
        else:
            value_format = ''
            name_format = quote_names and '"{0.name}"' or enum + "::{0.name}"
        return ',\n'.join((name_format + value_format).format(const) \
                          for const in constants)

    def _generate_enum(self, tenum):
        '''
        Generates code for an enumerated type. In C++, this is essentially the
        same as the thrift defn itself, using the enum keyword in C++.

        @param  tenum    The enumeration
        '''
        constants = tenum.constants
        # get the scope inside the namespace definitions
        s = self._types_scope
        if self.flag_compatibility:
            s('typedef {0} {1};'.format(self._namespace_prefix(
                        self._program.get_namespace('cpp')) + tenum.name,
                                        tenum.name))
            return
        primitive = s.defn('enum class {0.name}'.format(tenum), in_header=True)
        # what follows after the closing brace
        primitive.epilogue = ';\n\n'
        with primitive:
            out(self._generate_enum_constant_list(tenum.name, constants,
                    quote_names=False, include_values=True))
        # Generate a character array of enum values for debugging purposes.
        with s.impl('{0} _k{0}Values[] ='.format(tenum.name)):
            out(self._generate_enum_constant_list(tenum.name, constants,
                    quote_names=False, include_values=False))
        with s.impl('const char* _k{0}Names[] ='.format(tenum.name)):
            out(self._generate_enum_constant_list(tenum.name, constants,
                    quote_names=True, include_values=False))
        s.extern('const std::map<{0}, const char*> _{0}_VALUES_TO_NAMES'.format(
            tenum.name), value=('(apache::thrift::TEnumIterator<{0}>({1}, '
                '_k{0}Values, _k{0}Names), apache::thrift::TEnumIterator<{0}>('
                '-1, nullptr, nullptr));\n').format(tenum.name, len(constants)))
        s.extern('const std::map<const char*, {0}, apache::thrift::ltstr> '
                '_{0}_NAMES_TO_VALUES'.format(tenum.name),
                 value=('(apache::thrift::TEnumInverseIterator<{0}>({1}, '
                        '_k{0}Values, _k{0}Names), '
                        'apache::thrift::TEnumInverseIterator<{0}>'
                        '(-1, nullptr, nullptr));\n').format(
                                tenum.name, len(constants)))
        s()
        s.impl('\n')

        # specialize TEnumTraitsBase
        s.release()

        ns = self._namespace_prefix(self._get_namespace())
        fullName = ns + tenum.name
        minName = None
        maxName = None

        if len(constants) > 0:
            sortedConstants = sorted(constants, key=lambda c: c.value)
            minName = sortedConstants[0].name
            maxName = sortedConstants[-1].name

        self._generate_hash_equal_to(tenum)

        with self._types_global.namespace('apache.thrift').scope:

            with out().defn('template <> const char* TEnumTraitsBase<{fullName}>::'
                        'findName({fullName} value)'.format(**locals()),
                        name='findName'):
                out('return findName({ns}_{tenum.name}_VALUES_TO_NAMES, '
                    'value);'.format(**locals()))
            with out().defn('template <> bool TEnumTraitsBase<{fullName}>::'
                        'findValue(const char* name, {fullName}* outValue)'.
                        format(**locals()), name='findName'):
                out('return findValue({ns}_{tenum.name}_NAMES_TO_VALUES, '
                    'name, outValue);'.format(**locals()))
            if minName is not None and maxName is not None:
                with out().defn('template <> constexpr {fullName} '
                            'TEnumTraits<{fullName}>::min()'
                            .format(**locals()), name='min', in_header=True):
                    out('return {fullName}::{minName};'.format(**locals()))
                with out().defn('template <> constexpr {fullName} '
                            'TEnumTraits<{fullName}>::max()'
                            .format(**locals()), name='max', in_header=True):
                    out('return {fullName}::{maxName};'.format(**locals()))
        s = self._types_scope = \
                s.namespace(self._get_namespace()).scope
        s.acquire()

    def _generate_typedef(self, ttypedef):
        the_type = self._type_name(ttypedef.type, in_typedef=True,
                                   scope=self._types_scope)
        txt = 'typedef {0} {1};\n\n'.format(the_type, ttypedef.symbolic)
        # write it to the types scope
        self._types_scope(txt)

    def _declare_field(self, field, pointer=False, constant=False,
                        reference=False, unique=False, optional_wrapped=False):
        # I removed the 'init' argument, as all inits happen in default
        # constructor
        result = ''
        if constant:
            result += 'const '
        result += self._type_name(field.type)
        if pointer:
            result += '*'
        if reference:
            result += '&'
        if unique:
            result = "std::unique_ptr<" + result + ">"
        if optional_wrapped:
            result = "folly::Optional<" + result + ">"
        result += ' ' + field.name
        if not reference:
            result += ';'
        return result

    def _type_to_enum(self, ttype):
        'Converts the parse type to a C++ enum string for a given type.'
        t = self._get_true_type(ttype)
        suffix = None
        if t.is_base_type:
            base = t.as_base_type.base
            if base == t_base.void:
                raise ValueError('NO T_VOID CONSTRUCT')
            switch = {
                t_base.string: 'T_STRING',
                t_base.bool: 'T_BOOL',
                t_base.byte: 'T_BYTE',
                t_base.i16: 'T_I16',
                t_base.i32: 'T_I32',
                t_base.i64: 'T_I64',
                t_base.double: 'T_DOUBLE',
                t_base.float: 'T_FLOAT',
            }
            suffix = switch[base]
        elif t.is_enum:
            suffix = 'T_I32'
        elif t.is_struct or t.is_xception:
            suffix = 'T_STRUCT'
        elif t.is_map:
            suffix = 'T_MAP'
        elif t.is_set:
            suffix = 'T_SET'
        elif t.is_list:
            suffix = 'T_LIST'
        if suffix is None:
            raise TypeError('INVALID TYPE IN type_to_enum: ' + t.name)
        return 'apache::thrift::protocol::' + suffix

    # =====================================================================
    # SERVICE INTERFACE
    # =====================================================================

    def _generate_service(self, service):
        # open files and instantiate outputs
        context = self._make_context(service.name, True, True, True, True)
        self._out_tcc = context.tcc
        self._additional_outputs = context.additional_outputs
        self._custom_protocol_h = context.custom_protocol_h
        s = self._service_global = get_global_scope(CppPrimitiveFactory,
                                                    context)
        # Enter the scope (prints guard)
        s.acquire()
        s('#include <thrift/lib/cpp2/ServiceIncludes.h>')
        s('#include <thrift/lib/cpp2/async/HeaderChannel.h>')
        if not self.flag_bootstrap:
            s('#include <thrift/lib/cpp/TApplicationException.h>')
        s('#include <thrift/lib/cpp2/async/FutureRequest.h>')
        s('#include <folly/futures/Future.h>')
        s('#include "{0}"'.format(self._with_include_prefix(self._program,
                                                             self._program.name
                                                             + '_types.h')))
        print >>self._custom_protocol_h, \
                '#include "{0}"'.format(self._with_include_prefix(
                    self._program,
                    self._program.name + '_types_custom_protocol.h'))
        for _, b, _ in self.protocols:
            print >>context.additional_outputs[-1], \
                    '#include <thrift/lib/cpp2/protocol/{0}.h>'.format(b)
        print >>context.impl, '#include <thrift/lib/cpp2/protocol/Protocol.h>'
        for _, b, _ in self.protocols:
            print >>context.impl, \
                    '#include <thrift/lib/cpp2/protocol/{0}.h>'.format(b)
        s()
        if self.flag_compatibility:
            # Transform the cpp2 include prefix path into a cpp prefix path.
            s('#include "{0}.h"'
              .format(self._with_compatibility_include_prefix(self._program,
                                                    service.name)))
        # Include other Thrift includes
        for inc in self._program.includes:
            s('#include "{0}_types.h"' \
              .format(self._with_include_prefix(inc, inc.name)))
            print >>self._custom_protocol_h, \
                    '#include "{0}_types_custom_protocol.h"'.format(
                            self._with_include_prefix(inc, inc.name))
            if self.flag_implicit_templates:
                print >>self._out_tcc, '#include "{0}_types.tcc"'.format(
                    self._with_include_prefix(inc, inc.name))
        s()
        # Include custom headers
        for inc in self._program.cpp_includes:
            if inc.startswith('<'):
                s('#include {0}'.format(inc))
            else:
                s('#include "{0}"'.format(inc))

        if service.extends:
            s('#include "{0}.h"'.format(
                    self._with_include_prefix(service.extends.program,
                                              service.extends.name)))
            print >>self._custom_protocol_h, \
                    '#include "{0}_custom_protocol.h"'.format(
                            self._with_include_prefix(service.extends.program,
                                                      service.extends.name))
        s()
        s('namespace folly { ')
        s('  class IOBuf;')
        s('  class IOBufQueue;')
        s('}')
        s('namespace apache { namespace thrift {')
        s('  class Cpp2RequestContext;')
        s('  class BinaryProtocolReader;')
        s('  class CompactProtocolReader;')
        s('  namespace transport { class THeader; }')
        s('}}')
        s()

        # Open namespace
        s = s.namespace(self._get_namespace()).scope
        s.acquire()

        self._generate_service_helpers(service, s)
        self._generate_service_server_interface_async(service, s)
        s('class ' + service.name + 'AsyncProcessor;')
        self._generate_service_server_interface(service, s)
        self._generate_service_server_null(service, s)
        self._generate_processor(service, s)
        self._generate_service_client(service, s)

        # make sure that the main types namespace is closed
        s.release()

        self._generate_service_helpers_serializers(service, s)

        if self.flag_implicit_templates:
            # Include the types.tcc file from the types header file
            s = self._service_global
            s()
            s('#include "{0}.tcc"'.format(
                self._with_include_prefix(self._program, service.name)))
            self._service_global.release()

    def _generate_service_helpers_serializers(self, service, s):
        s = s.namespace('apache.thrift').scope
        s.acquire()
        for function in service.functions:
            arglist = function.arglist
            if self.flag_compatibility:
                if self.flag_stack_arguments:
                    arglist.name = "{0}_{1}_args".format(service.name, function.name)
                    self._generate_cpp2ops(True, arglist, s)
                arglist.name = "{0}_{1}_pargs".format(service.name, function.name)
                self._generate_cpp2ops(True, arglist, s)

                if not function.oneway:
                    result = self._get_presult_object(service, function)
                    self._generate_cpp2ops(True, result, s)
        s.release()

    def _get_presult_object(self, service, function):
        result = frontend.t_struct(
            self.program,
            "{0}_{1}_presult".format(service.name, function.name))
        success = frontend.t_field(function.returntype, "success", 0)
        if not function.returntype.is_void:
            result.append(success)
        xs = function.xceptions
        for field in xs.members:
            result.append(field)
        return result



    def _generate_service_helpers(self, service, s):
        for function in service.functions:
            arglist = function.arglist
            if self.flag_compatibility:
                name_orig = arglist.name
                if self.flag_stack_arguments:
                    arglist.name = "{0}_{1}_args".format(service.name, function.name)
                    self._generate_struct_complete(s, arglist,
                                                   is_exception=False,
                                                   pointers=False,
                                                   read=True,
                                                   write=True,
                                                   swap=False,
                                                   result=False,
                                                   to_additional=True,
                                                   simplejson=False)
                arglist.name = "{0}_{1}_pargs".format(service.name, function.name)
                self._generate_struct_complete(s, arglist,
                                               is_exception=False,
                                               pointers=True,
                                               read=True,
                                               write=True,
                                               swap=False,
                                               result=False,
                                               has_isset=False,
                                               to_additional=True,
                                               simplejson=False)
                arglist.name = name_orig
            else:
                def generate_pargs(suffix, pargs):
                    flist = ['apache::thrift::FieldData<{0}, {1}, {2}{3}>'.format(
                                arg.key,
                                self._type_to_enum(arg.type),
                                self._type_name(arg.type),
                                suffix)
                                    for arg in arglist.members]
                    pargs = "{0}_{1}_{2}".format(service.name, function.name, pargs)
                    flist.insert(0, 'false')
                    fstr = ", ".join(flist)
                    print >>self._out_tcc, \
                        'typedef apache::thrift::ThriftPresult<{0}> {1};'.format(
                            fstr, pargs)
                if self.flag_stack_arguments:
                    generate_pargs('', 'args')
                generate_pargs('*', 'pargs')


            if not function.oneway:
                if self.flag_compatibility:
                    # WTF Using _get_presult_object causes a segmentation fault?
                    result = frontend.t_struct(
                         self.program,
                         "{0}_{1}_presult".format(service.name, function.name))
                    success = frontend.t_field(function.returntype, "success", 0)
                    if not function.returntype.is_void:
                        result.append(success)
                    xs = function.xceptions
                    for field in xs.members:
                        result.append(field)
                    self._generate_struct_complete(s, result,
                                                   is_exception=False,
                                                   pointers=True,
                                                   read=True,
                                                   write=True,
                                                   swap=False,
                                                   result=True,
                                                   to_additional=True,
                                                   simplejson=False)
                else:
                    flist = ['apache::thrift::FieldData<{0}, '
                             'apache::thrift::protocol::T_STRUCT, {1}>'.format(
                                field.key, self._type_name(field.type))
                                    for field in function.xceptions.members]
                    if not function.returntype.is_void:
                        type = self._type_name(function.returntype)
                        ttype = self._type_to_enum(function.returntype)
                        flist.insert(0, 'apache::thrift::FieldData<0, {0}, {1}*>'
                            .format(ttype, type))

                    presult = "{0}_{1}_presult".format(service.name, function.name)
                    flist.insert(0, 'true')
                    fstr = ", ".join(flist)

                    print >>self._out_tcc, \
                        'typedef apache::thrift::ThriftPresult<{0}> {1};'.format(
                            fstr, presult)

    def _generate_service_client(self, service, s):
        classname = service.name + "AsyncClient"
        if not service.extends:
            class_signature = 'class ' + classname + \
                ' : public apache::thrift::TClientBase'
        else:
            class_signature = 'class ' + classname + \
                ' : public ' + self._type_name(service.extends) +\
                'AsyncClient'
        with s.cls(class_signature):
            out().label('public:')

            with out().defn('const char* {name}()',
                    name='getServiceName',
                    modifiers='virtual',
                    output=self._additional_outputs[-1]):
                out("return \"{0}\";".format(service.name))

            out("typedef std::unique_ptr<apache::thrift::RequestChannel"
              ", folly::DelayedDestruction::Destructor>"
              " channel_ptr;")
            init = OrderedDict()
            if service.extends:
                init[self._type_name(service.extends) + 'AsyncClient'] = \
                    'channel'
            if not service.extends:
                init["channel_"] = "channel"
            # TODO: make it possible to create a connection context from a
            # thrift channel
            out().defn('~{name}()', name=classname,
                   modifiers='virtual', in_header=True).scope.empty()

            with out().defn('{name}(std::shared_ptr<' +
                            'apache::thrift::RequestChannel> channel)',
                        name=classname,
                        init_dict=init,
                        in_header=True):
                if not service.extends:
                    out('connectionContext_.reset('
                            'new apache::thrift::Cpp2ConnContext);')

            if not service.extends:
                with out().defn('apache::thrift::RequestChannel* '
                            ' {name}()', name='getChannel',
                            in_header=True):
                    out("return this->channel_.get();")
                with out().defn('apache::thrift::HeaderChannel* '
                            ' {name}()', name='getHeaderChannel',
                            in_header=True):
                    out("return dynamic_cast<apache::thrift::HeaderChannel*>"
                            "(this->channel_.get());")


            # Write out all the functions
            for function in service.functions:
                self._generate_client_async_function(service, function)
                self._generate_client_async_function(service, function,
                                                     uses_rpc_options=True)

                if not self._is_stream_type(function.returntype):
                    self._generate_client_sync_function(service, function)
                    self._generate_client_sync_function(service, function,
                                                        uses_rpc_options=True)
                    self._generate_client_future_function(service, function)
                    self._generate_client_future_function(service, function,
                                                          uses_rpc_options=True)
                    if not function.oneway:
                        self._generate_client_future_function(
                                service, function,
                                uses_rpc_options=True, header=True)

                self._generate_client_std_function(function)

                if self._is_stream_type(function.returntype):
                    self._generate_client_streaming_function(service, function)
                    self._generate_client_streaming_function(service, function,
                        uses_rpc_options=True)

                if not function.oneway:
                    self._generate_recv_functions(function)

                self._generate_templated_client_function(service, function)

                if not function.oneway:
                    self._generate_templated_recv_function(service, function)

            if not service.extends:
                out().label('protected:')
                out("std::unique_ptr<apache::thrift::Cpp2ConnContext> "
                  "connectionContext_;")
                out("std::shared_ptr<apache::thrift::RequestChannel> channel_;")

    def _get_async_func_name(self, function):
        if self._is_processed_in_eb(function):
            return 'async_eb_' + function.name
        else:
            return 'async_tm_' + function.name

    def _generate_service_server_null(self, service, s):
        classname = service.name + "SvNull"
        if not service.extends:
            class_signature = "class " + classname + " : public " + \
                service.name + \
                "SvIf"
        else:
            class_signature = "class " + classname + " : public " + \
                service.name + "SvIf, virtual public " + \
                self._type_name(service.extends) + "SvIf"
        with s.cls(class_signature):
            out().label('public:')
            out().defn('~{0}()'.format(classname), name=classname,
                   modifiers='virtual',
                   in_header=True).scope.empty()
            for function in service.functions:
                if not self._is_processed_in_eb(function) and \
                   not self._is_stream_type(function.returntype):
                    with out().defn(
                            self._get_process_function_signature(service,
                                                                 function,
                                                                 True),
                            name=function.name,
                            modifiers='virtual'):
                        if not function.oneway and \
                          not function.returntype.is_void and \
                          not self._is_complex_type(function.returntype):
                            out('return ' +
                              self._default_value(function.returntype) + ';')

    def _generate_service_server_interface_async(self, service, s):
        classname = service.name + "SvAsyncIf"
        class_signature = "class " + classname
        with s.cls(class_signature):
            out().label('public:')
            out().defn('~{0}()'.format(classname), name=classname,
                   modifiers='virtual',
                   in_header=True).scope.empty()
            for function in service.functions:
                out().defn(self._get_process_function_signature_async(service,
                                                                  function),
                       name=self._get_async_func_name(function),
                       modifiers='virtual',
                       pure_virtual=True)

                # TODO: Remove this once everything has migrated to async_eb or
                # async_tm
                out().defn(self._get_process_function_signature_async(service,
                                                                  function),
                       name="async_" + function.name,
                       modifiers='virtual',
                       delete=True)
                if not self._is_stream_type(function.returntype):
                    out().defn(self._get_process_function_signature_future(
                           service, function),
                           name="future_" + function.name,
                           modifiers='virtual',
                           pure_virtual=True)

    def _get_function_priority(self, service, function):
        if function.annotations is not None and \
                'priority' in function.annotations.annotations:
            return function.annotations.annotations['priority']
        elif 'priority' in service.annotations:
            return service.annotations['priority']
        else:
            return 'NORMAL'

    def _generate_service_server_interface(self, service, s):
        classname = service.name + "SvIf"
        if not service.extends:
            class_signature = "class " + classname + " : public " + \
                service.name + \
                "SvAsyncIf, public apache::thrift::ServerInterface"
        else:
            class_signature = "class " + classname + " : public " + \
                service.name + "SvAsyncIf, virtual public " + \
                self._type_name(service.extends) + "SvIf"
        with s.cls(class_signature):
            out().label('public:')
            out('typedef ' + service.name + 'AsyncProcessor ProcessorType;')
            out().defn('~{0}()'.format(classname), name=classname,
                   modifiers='virtual',
                   in_header=True).scope.empty()
            with out().defn('std::unique_ptr<apache::thrift::AsyncProcessor>' +
                        ' {name}()',
                        name='getProcessor',
                        modifiers='virtual'):
                out('return folly::make_unique<{klass}AsyncProcessor>(this);'
                    .format(klass=service.name))
            for function in service.functions:
                if not self._is_stream_type(function.returntype):
                    with out().defn(self._get_process_function_signature(service,
                                                                     function,
                                                                     True),
                                name=function.name,
                                modifiers='virtual'):
                        out('throw apache::thrift::TApplicationException('
                          '"Function {0} is unimplemented");'
                          .format(function.name))
                    self._generate_server_future_function(service, function)
                self._generate_server_async_function(service, function)

    def _generate_server_future_function(self, service, function):
        name = "future_" + function.name
        sig = self._get_process_function_signature_future(service, function)
        with out().defn(sig, name=name):
            rettype = self._type_name(function.returntype)
            if self.flag_stack_arguments:
                args = [m.name for m in function.arglist.members]
                if not self._is_complex_type(function.returntype):
                    f = "future"
                    c = "[&] {{ return {n}({a}); }}"
                else:
                    f = "future_returning"
                    args = ["_return"] + args
                    c = "[&]({r}& _return) {{ {n}({a}); }}"
            else:
                mv = lambda n: "std::move({n})".format(n=n)
                args = [
                    mv(m.name) if self._is_complex_type(m.type) else m.name
                    for m in function.arglist.members
                ]
                if not self._is_complex_type(function.returntype):
                    f = "future"
                    c = "[&] {{ return {n}({a}); }}"
                else:
                    f = "future_returning_uptr"
                    args = ["_return"] + args
                    c = "[&]({r}& _return) {{ {n}({a}); }}"
            s = "return {ns}::{f}(" + c + ");"
            ns = "apache::thrift::detail::si"
            r = self._type_name(function.returntype)
            out(s.format(ns=ns, f=f, n=function.name, r=r, a=", ".join(args)))

    def _generate_server_async_function_streaming(self, function):
        out('callback->exception(folly::make_exception_wrapper<'
            'apache::thrift::TApplicationException>('
            '"Function {0} is unimplemented"));'.format(function.name))

    def _generate_server_async_function_future(self, function):
        if self._is_stream_type(function.returntype):
            return self._generate_server_async_function_streaming(function)
        stack_args = self.flag_stack_arguments
        is_complex_type = lambda t: self._is_complex_type(t) and not stack_args
        if self._is_processed_in_eb(function):
            for arg in function.arglist.members:
                if is_complex_type(arg.type):
                    out("auto {n}_ = folly::makeMoveWrapper({n});"
                        .format(n=arg.name))
            f = "async_eb_oneway" if function.oneway else "async_eb"
            mv = lambda n: "{n}_.move()".format(n=n)
            c = "[=]() mutable {{ return future_{n}({a}); }}"
        else:
            f = "async_tm_oneway" if function.oneway else "async_tm"
            mv = lambda n: "std::move({n})".format(n=n)
            c = "[&] {{ return future_{n}({a}); }}"
        s = "{ns}::{f}(this, std::move(callback), " + c + ");"
        args = [
            mv(m.name) if is_complex_type(m.type) else m.name
            for m in function.arglist.members
        ]
        ns = "apache::thrift::detail::si"
        out(s.format(ns=ns, f=f, n=function.name, a=", ".join(args)))

    def _generate_server_async_function(self, service, function):
        with out().defn(self._get_process_function_signature_async(service,
                                                                   function),
                    name=self._get_async_func_name(function),
                    modifiers='virtual'):
            self._generate_server_async_function_future(function)

    def _get_process_function_signature_async(self, service, function):
        sig = 'void {name}('
        if function.oneway:
            sig += 'std::unique_ptr<apache::thrift::HandlerCallbackBase>' + \
                ' callback'
        else:
            sig += 'std::unique_ptr<{0}> callback'.format(
                    self._get_handler_callback_class(function))

        sig += self._argument_list(function.arglist, True, unique=True)
        sig += ')'
        return sig

    def _get_process_function_signature_future(self, service, function):
        rettype = self._type_name(function.returntype)
        if self._is_complex_type(function.returntype) and \
                not self.flag_stack_arguments:
            rettype = 'std::unique_ptr<' + rettype + '>'
        sig = 'folly::Future<' + \
            _lift_unit(rettype) + '> {name}('

        sig += self._argument_list(function.arglist, False, unique=True)
        sig += ')'
        return sig

    def _get_process_function_signature(self, service, function,
                                        no_param_names=False):
        addcomma = False
        if not function.oneway:
            if self._is_complex_type(function.returntype):
                sig = 'void {name}' + '({0}& {1}'.format(
                    self._type_name(function.returntype),
                    '/*_return*/' if no_param_names else '_return')
                addcomma = True
            else:
                sig = self._type_name(function.returntype)
                sig += ' {name}('
        else:
            sig = 'void {name}('
        sig += self._argument_list(function.arglist, addcomma, unique=True,
                                   no_param_names=no_param_names)
        sig += ')'
        return sig

    def _generate_app_ex(self, service, errorstr, functionname, seqid, is_in_eb,
                         s, reqCtx, err_code=None, uex_ew=None):
        with out('if (req)'):
            out('LOG(ERROR) << {0} << " in function {1}";'.format(
                    errorstr, functionname))
            code = '' if err_code is None else \
                    'apache::thrift::TApplicationException::' \
                    'TApplicationExceptionType::' + err_code + ', '
            out('apache::thrift::TApplicationException x({0}{1});'.
                format(code, errorstr))
            if uex_ew:
                ctx = 'ctx'
                out('ctx->userExceptionWrapped(false, {});'.format(uex_ew))
                out('ctx->handlerErrorWrapped({});'.format(uex_ew))
            else:
                ctx = 'nullptr'
            out('folly::IOBufQueue queue = serializeException("{0}", &prot, {1}, {2}, '
                'x);'.format(functionname, seqid, ctx))
            out('queue.append(apache::thrift::transport::THeader::transform('
                'queue.move(), '
                '{0}->getHeader()->getWriteTransforms(), '
                '{0}->getHeader()->getMinCompressBytes()));'.format(reqCtx))
            if is_in_eb:
                out('req->sendReply(queue.move());')
            else:
                out('auto queue_mw = '
                        'folly::makeMoveWrapper(std::move(queue));')
                out('auto req_mw = folly::makeMoveWrapper(std::move(req));')
                with out('eb->runInEventBaseThread([=]() mutable'):
                    out('(*req_mw)->sendReply(queue_mw->move());')
                out(');')
            out('return;')
        with out('else'):
            out('LOG(ERROR) << {0} << " in oneway function {1}";'.format(
                    errorstr, functionname))

    def _generate_process_function(self, service, function):
        if function.oneway:
            if self._is_processed_in_eb(function):
                # Old clients may not send the special
                # oneway id, so we need to send a fake
                # response to them while in event base.
                with out('if (!req->isOneway())'):
                    out('req->sendReply(std::unique_ptr<folly::IOBuf>());')
        out("// make sure getConnectionContext is null")
        out("// so async calls don't accidentally use it")
        out('iface_->setConnectionContext(nullptr);')
        aprefix = 'uarg_'
        if self.flag_stack_arguments:
            out('{0}_{1}_args args;'.format(service.name, function.name))
        else:
            out('{0}_{1}_pargs args;'.format(service.name, function.name))
        for idx, field in enumerate(function.arglist.members):
            val = ""
            t = self._get_true_type(field.type)
            if t.is_base_type or t.is_enum:
                val = self._member_default_value(field)
            if self.flag_stack_arguments:
                pass
            elif self._is_complex_type(field.type):
                out('std::unique_ptr<{0}> {1}(new {0}({2}));'.format(
                        self._type_name(field.type), aprefix + field.name, val))
                out('{0} = {1}.get();'.format(
                        self._get_pargs_field(idx, field),
                        aprefix + field.name))
            else:
                # use uniform initialization syntax to avoid most vexing parse
                out('{0} {1}{{{2}}};'.format(
                        self._type_name(field.type), aprefix + field.name, val))
                out('{0} = &{1};'.format(
                        self._get_pargs_field(idx, field),
                        aprefix + field.name))
        out(('std::unique_ptr<apache::thrift::' +
           'ContextStack> c(this->getContextStack' +
           '(this->getServiceName(), "{0}.{1}", ctx));'
           ).format(service.name, function.name))
        out("")
        with out('try'):
            out('deserializeRequest(args, buf.get(), iprot.get(), c.get());')
        with out('catch (const std::exception& ex)'):
            if function.oneway:
                out('LOG(ERROR) << ex.what() << " in function noResponse";')
                out('return;')
            else:
                out('ProtocolOut_ prot;')
                self._generate_app_ex(service, 'ex.what()',
                                      function.name, "iprot->getSeqId()",
                                      False, out, 'ctx',
                                      'PROTOCOL_ERROR')
        args = []
        for idx, member in enumerate(function.arglist.members):
            if self.flag_stack_arguments:
                args.append(self._get_pargs_field(idx, member))
            elif self._is_complex_type(member.type):
                args.append("std::move({0})".format(
                        aprefix + member.name))
            else:
                args.append(self._get_pargs_field(idx, member, False))
        if function.oneway:
            out('std::unique_ptr<apache::thrift::HandlerCallbackBase> callback(' +
              'new apache::thrift::HandlerCallbackBase(std::move(req), ' +
              'std::move(c), nullptr, nullptr, eb, tm, ctx));')
        else:
            cb_class = self._get_handler_callback_class(function)
            out(('auto callback = folly::make_unique<{0}>(std::move(req), ' +
               'std::move(c), return_{1}<ProtocolIn_,' +
               'ProtocolOut_>, throw_{1}<ProtocolIn_,' +
               ' ProtocolOut_>, throw_wrapped_{1}<ProtocolIn_,' +
               ' ProtocolOut_>, iprot->getSeqId(),' +
               ' eb, tm, ctx);').format(cb_class, function.name))
        # Oneway request won't be canceled if expired. see D1006482 for
        # further details. TODO: fix this
        if not self._is_processed_in_eb(function) and not function.oneway:
            with out('if (!callback->isRequestActive())'):
                out('callback.release()->deleteInThread();')
                out('return;')
        args.insert(0, 'std::move(callback)')
        out('ctx->setStartedProcessing();')
        out('iface_->{0}({1});'.format(self._get_async_func_name(function),
                                     ", ".join(args)))

    def _generate_processor(self, service, s):
        if not service.extends:
            class_signature = 'class {0} : '.format(
                service.name + 'AsyncProcessor') + \
                'public ::apache::thrift::GeneratedAsyncProcessor'
        else:
            class_signature = 'class {0} : '.format(
                service.name + 'AsyncProcessor') + \
                'public ' + self._type_name(service.extends) + \
                'AsyncProcessor'
        with s.cls(class_signature):
            out().label('public:')
            with out().defn('const char* {name}()', name='getServiceName',
                        modifiers='virtual'):
                out("return \"{0}\";".format(service.name))

            base_of = lambda n: "{}AsyncProcessor".format(self._type_name(n))
            base = base_of(service.extends) if service.extends else "void"
            out('using BaseAsyncProcessor = {};'.format(base))

            out().label('protected:')

            out('{0}SvIf* iface_;'.format(service.name))
            with out().defn('folly::Optional<std::string> {name}(' +
                        'folly::IOBuf* buf, ' +
                        'apache::thrift::protocol::PROTOCOL_TYPES protType)',
                        name='getCacheKey',
                        modifiers='virtual'):
                out('return apache::thrift::detail::ap::get_cache_key(' +
                    'buf, protType, cacheKeyMap_);')

            out().label('public:')

            with out().defn('void {name}(std::unique_ptr<' +
                        'apache::thrift::ResponseChannel::Request> req, ' +
                        'std::unique_ptr<folly::IOBuf> buf, ' +
                        'apache::thrift::protocol::PROTOCOL_TYPES protType, ' +
                        'apache::thrift::Cpp2RequestContext* context, ' +
                        'folly::EventBase* eb, ' +
                        'apache::thrift::concurrency::ThreadManager* tm)',
                        name='process',
                        modifiers='virtual'):
                out('apache::thrift::detail::ap::process(' +
                    'this, std::move(req), std::move(buf), protType, ' +
                    'context, eb, tm);')

            out().label('protected:')

            with out().defn('bool {name}(const folly::IOBuf* buf, ' +
                    'const apache::thrift::transport::THeader* header)',
                        name='isOnewayMethod',
                        modifiers='virtual'):
                out('return apache::thrift::detail::ap::is_oneway_method(' +
                    'buf, header, onewayMethods_);')

            out().label('private:')
            oneways = out().defn('std::unordered_set<std::string> {name}',
                                 name='onewayMethods_',
                                 modifiers='static')
            oneways.epilogue = ';\n'
            with oneways:
                out(',\n'.join('"' + function.name + '"'
                        for function in service.functions if function.oneway))
            cache_map_type = 'std::unordered_map<std::string, int16_t>'
            cache_map = out().defn(cache_map_type + ' {name}',
                    name='cacheKeyMap_',
                    modifiers='static')
            cache_map.epilogue = ';'

            cache_member_map = {}
            for function in service.functions:
                cache_key = self._get_cache_key(function)
                if cache_key is not None:
                    if not cache_key.type.is_string:
                        raise CompilerError('Cache annotation is only \
                                allowed on string types')
                    cache_member_map[function.name] = cache_key.key
            with cache_map:
                out(',\n'.join("{{\"{}\", {}}}".format(function, cache_key)
                        for function, cache_key in
                        cache_member_map.iteritems()))
            prot = 0
            for shortprot, protname, prottype in self.protocols:
                if shortprot == 'simple_json':
                    continue
                out().label('public:')
                out((
                    'using {proto}ProcessFunc = ' +
                    'ProcessFunc<{klass}AsyncProcessor, ' +
                    'apache::thrift::{proto}Reader>;')
                    .format(proto=protname, klass=service.name))
                out('using {proto}ProcessMap = ProcessMap<{proto}ProcessFunc>;'
                    .format(proto=protname))
                map_type = '{klass}AsyncProcessor::{proto}ProcessMap' \
                    .format(proto=protname, klass=service.name)
                map_name = '{0}ProcessMap_'.format(shortprot)
                with out().defn('const ' + map_type + '& {name}()',
                                name='get{proto}ProcessMap'
                                    .format(proto=protname),
                                modifiers='static'):
                    out('return {proto}ProcessMap_;'.format(proto=shortprot))
                out().label('private:')
                process_map = out().defn(map_type + ' {name}',
                                         name=map_name,
                                         modifiers='static')
                if self.flag_separate_processmap:
                    process_map.output = self._additional_outputs[prot]
                if prot < 1:  # TODO: fix build tool to use more than 2 outputs
                    prot = prot + 1
                process_map.epilogue = ';'

                with process_map:
                    out(',\n'.join('{"' + function.name + '", &' +
                        service.name + 'AsyncProcessor::' +
                        self._get_handler_function_name(function) +
                        '<apache::thrift::{0}Reader, '
                        'apache::thrift::{0}Writer>}}'.format(protname)
                            for function in service.functions))

            out().label('private:')
            for function in service.functions:
                loadname = '"{0}.{1}"'.format(service.name, function.name)
                if not self._is_processed_in_eb(function):
                    with out().defn(
                            'template <typename ProtocolIn_, '
                            'typename ProtocolOut_>\n'
                            'void {name}(std::unique_ptr<'
                            'apache::thrift::ResponseChannel::Request> req, '
                            'std::unique_ptr<folly::IOBuf> buf, '
                            'std::unique_ptr<ProtocolIn_> iprot, '
                            'apache::thrift::Cpp2RequestContext* ctx, '
                            'folly::EventBase* eb, '
                            'apache::thrift::concurrency::ThreadManager* tm'
                            ')',
                            name="_processInThread_{0}"
                                .format(function.name),
                            output=self._out_tcc):
                        out('auto pri = iface_->getRequestPriority(ctx, '
                            'apache::thrift::concurrency::{0});'.format(
                                self._get_function_priority(service, function)))
                        out('processInThread<ProtocolIn_, ProtocolOut_>' +
                          '(std::move(req), std::move(buf),' +
                          'std::move(iprot), ctx, eb, tm, pri, '
                          + (function.oneway and 'true' or 'false') +
                          ', &{0}AsyncProcessor::process_{1}'.format(
                                  service.name, function.name) +
                          '<ProtocolIn_, ProtocolOut_>, this);')

                with out().defn('template <typename ProtocolIn_, ' +
                            'typename ProtocolOut_>\n' +
                            'void {name}(std::unique_ptr<' +
                            'apache::thrift::ResponseChannel::Request> req, ' +
                            'std::unique_ptr<folly::IOBuf> buf, ' +
                            'std::unique_ptr<ProtocolIn_> iprot,' +
                            'apache::thrift::Cpp2RequestContext* ctx,' +
                            'folly::EventBase* eb, ' +
                            'apache::thrift::concurrency::ThreadManager* tm)',
                            name="process_{0}".format(function.name),
                            output=self._out_tcc):
                    self._generate_process_function(service, function)

                if not function.oneway:
                    args = [
                        'int32_t protoSeqId',
                        'apache::thrift::ContextStack* ctx']

                    if not function.returntype.is_void:
                        args.append("{0} const& _return".format(
                                self._type_name(function.returntype)))

                    with out().defn(
                        'template <class ProtocolIn_, class ProtocolOut_>\n' +
                                'folly::IOBufQueue {name}' + '({0})'
                                .format(", ".join(args)),
                                name="return_{0}".format(function.name),
                                output=self._out_tcc,
                                modifiers='static'):
                        out('ProtocolOut_ prot;')
                        out('{0}_{1}_presult result;'.format(
                                service.name, function.name))
                        if self._function_produces_result(function):
                            out('{0} = const_cast<{1}*>(&_return);'.format(
                                self._get_presult_success(),
                                self._type_name(function.returntype)))
                            out('{0};'.format(
                                self._get_presult_success_isset('true')))
                        out('return serializeResponse("{0}", '
                          '&prot, protoSeqId, ctx, result);'
                          .format(function.name))

                def cast_xceptions(xceptions):
                    for idx, xception in enumerate(xceptions):
                        with out('catch (const {0}& e)'.format(
                            self._type_name(xception.type))):
                            ew = 'folly::exception_wrapper(e)'
                            out('ctx->userExceptionWrapped(true, {});'.format(ew))
                            ex_idx = self._exception_idx(function, idx)
                            out('{0} = e;'.format(
                                self._get_presult_exception(ex_idx, xception)))
                            out('{0};'.format(
                                self._get_presult_exception_isset(ex_idx, xception, 'true')))
                if not function.oneway:
                    with out().defn(
                        'template <class ProtocolIn_, class ProtocolOut_>\n' +
                        'void {name}(std::unique_ptr' +
                        '<apache::thrift::ResponseChannel::Request> req,' +
                        'int32_t protoSeqId,' +
                        'apache::thrift::ContextStack* ctx,' +
                        'std::exception_ptr ep,' +
                        'apache::thrift::Cpp2RequestContext* reqCtx)',
                                name="throw_{0}".format(function.name),
                                modifiers='static',
                                output=self._out_tcc):
                        out('ProtocolOut_ prot;')
                        if len(function.xceptions.members) > 0:
                            out('{0}_{1}_presult result;'.format(
                                    service.name, function.name))
                        with out('try'):
                            out('std::rethrow_exception(ep);')
                        cast_xceptions(
                            function.xceptions.members)
                        with out('catch (const std::exception& e)'):
                            out('auto ew = folly::exception_wrapper(ep, e);')
                            self._generate_app_ex(
                                service,
                                "folly::exceptionStr(e)." +
                                "toStdString()",
                                function.name, "protoSeqId", True,
                                out(), 'reqCtx',
                                uex_ew='ew')
                        with out('catch (...)'):
                            self._generate_app_ex(
                                service,
                                "\"<unknown exception>\"",
                                function.name, "protoSeqId", True,
                                out(), 'reqCtx')
                        if len(function.xceptions.members) > 0:
                            out('auto queue = serializeResponse('
                              '"{0}", &prot, protoSeqId, ctx,'
                              ' result);'.format(function.name))
                            out('queue.append('
                                'apache::thrift::transport::THeader::transform('
                                'queue.move(), '
                                '{0}->getHeader()->getWriteTransforms(), '
                                '{0}->getHeader()->getMinCompressBytes()));'.format('reqCtx'))
                            out('return req->sendReply(queue.move());')
                    with out().defn(
                        'template <class ProtocolIn_, class ProtocolOut_>\n' +
                        'void {name}(std::unique_ptr' +
                        '<apache::thrift::ResponseChannel::Request> req,' +
                        'int32_t protoSeqId,' +
                        'apache::thrift::ContextStack* ctx,' +
                        'folly::exception_wrapper ew,' +
                        'apache::thrift::Cpp2RequestContext* reqCtx)',
                        name="throw_wrapped_{0}".format(function.name),
                        modifiers='static',
                        output=self._out_tcc
                    ):
                        with out('if (!ew)'):
                            out('return;')

                        out('ProtocolOut_ prot;')
                        if len(function.xceptions.members) > 0:
                            out('{0}_{1}_presult result;'.format(
                                service.name, function.name))
                        for idx, xception in enumerate(function.xceptions.members):
                            xception_type = self._type_name(
                                xception.type)
                            with out('if (ew.with_exception<{0}>([&]({0}& e)'.
                                     format(xception_type)):
                                out('ctx->userExceptionWrapped(true, ew);')
                                ex_idx = self._exception_idx(function, idx)
                                out('{0} = e;'.format(
                                    self._get_presult_exception(ex_idx, xception)))
                                out('{0};'.format(
                                    self._get_presult_exception_isset(ex_idx, xception, 'true')))
                            out(')) {} else ')
                        with out(' '):
                            self._generate_app_ex(
                                service,
                                'ew.what().toStdString()',
                                function.name, "protoSeqId", True,
                                out(), 'reqCtx', None,
                                uex_ew='ew')
                        if len(function.xceptions.members) > 0:
                            out('auto queue = serializeResponse('
                                '"{0}", &prot, protoSeqId, ctx,'
                                ' result);'.format(function.name))
                            out('queue.append('
                                'apache::thrift::transport::THeader::transform('
                                'queue.move(), '
                                '{0}->getHeader()->getWriteTransforms(), '
                                '{0}->getHeader()->getMinCompressBytes()));'.format('reqCtx'))
                            out('return req->sendReply(queue.move());')


            out().label('public:')
            init = OrderedDict()
            if service.extends:
                init[self._type_name(service.extends) + 'AsyncProcessor'] = \
                    'iface'
            init['iface_'] = 'iface'
            out().defn('{name}(' + service.name + 'SvIf* iface)',
                   name=service.name + 'AsyncProcessor',
                   init_dict=init,
                   in_header=True).scope.empty()
            out().defn('{name}()', name='~' + service.name + 'AsyncProcessor',
                   in_header=True, modifiers='virtual').scope.empty()

    def _get_handler_function_name(self, function):
        if self._is_processed_in_eb(function):
            return 'process_' + function.name
        else:
            return '_processInThread_' + function.name

    def _generate_client_sync_function(self, service, function,
                                       uses_rpc_options=False):

        signature = self._get_sync_function_signature(function,
                                                      uses_rpc_options)

        with out().defn(signature,
                name="sync_" + function.name,
                modifiers='virtual',
                output=self._additional_outputs[-1]):
            common_args = [arg.name for arg in function.arglist.members]

            if not uses_rpc_options:
                out('::apache::thrift::RpcOptions rpcOptions;')
                if function.returntype.is_void:
                    args = ["rpcOptions"]
                    args.extend(common_args)
                    args_list = ", ".join(args)
                    out("sync_{name}({args_list});".format(name=function.name,
                                                         args_list=args_list))
                elif not self._is_complex_type(function.returntype):
                    args = ["rpcOptions"]
                    args.extend(common_args)
                    args_list = ", ".join(args)

                    out("return sync_{name}({args_list});"
                         .format(name=function.name, args_list=args_list))
                else:
                    args = ["rpcOptions", "_return"]
                    args.extend(common_args)
                    args_list = ", ".join(args)

                    out("sync_{name}({args_list});"
                         .format(name=function.name, args_list=args_list))

            else:
                out('apache::thrift::ClientReceiveState _returnState;')

                sync_callback_name = self.tmp("callback")
                out("auto {sync_callback_name} = "
                    "folly::make_unique<apache::thrift::ClientSyncCallback>("
                    "&_returnState, getChannel()->getEventBase(), {isOneWay});"
                  .format(sync_callback_name=sync_callback_name,
                      isOneWay=str(function.oneway).lower()))

                args = ["rpcOptions",
                        "std::move({0})".format(sync_callback_name)]
                args.extend(common_args)
                args_list = ", ".join(args)

                out("{name}({args_list});".format(name=function.name,
                                                args_list=args_list))

                out("getChannel()->getEventBase()->loopForever();")

                if not function.oneway:
                    out("SCOPE_EXIT {")
                    out("  if (_returnState.header() && "
                            "!_returnState.header()->getHeaders().empty()) {")
                    out("    rpcOptions.setReadHeaders("
                            "_returnState.header()->releaseHeaders());")
                    out("  }")
                    out("};")

                    with out("if (!_returnState.buf())"):
                        out("assert(_returnState.exception());")
                        out("std::rethrow_exception(_returnState.exception());")

                    if not function.returntype.is_void:
                        if not self._is_complex_type(function.returntype):
                            out("return recv_{}(_returnState);"
                                    .format(function.name))
                        else:
                            out("recv_{}(_return, _returnState);"
                                    .format(function.name))
                    else:
                        out("recv_{}(_returnState);".format(function.name))

    def _get_sync_function_signature(self, function, uses_rpc_options=False):
        params = []

        if uses_rpc_options:
            params.append("apache::thrift::RpcOptions& rpcOptions")

        if function.returntype.is_void:
            return_type = "void"
        elif not self._is_complex_type(function.returntype):
            return_type = self._type_name(function.returntype)
        else:
            return_type = "void"
            params.append(self._type_name(function.returntype) + "& _return")

        param_list = ", ".join(params)
        param_list += self._argument_list(function.arglist,
                                          add_comma=bool(params),
                                          unique=False)

        return return_type + " {name}(" + param_list + ")"

    def _generate_client_nonrpcoptions_function(self, service, function,
                                                function_name):
        out("::apache::thrift::RpcOptions rpcOptions;")
        args = ["rpcOptions"]

        args.extend([arg.name for arg in function.arglist.members])
        args_list = ", ".join(args)

        out("return {function}({args});"
              .format(function=function_name, args=args_list))

    def _generate_client_future_function(self, service, function,
                                         uses_rpc_options=False, header=False):

        if header:
            function_name = "header_future_" + function.name
        else:
            function_name = "future_" + function.name
        signature = self._get_future_function_signature(function,
                                                        uses_rpc_options,
                                                        header)
        with out().defn(signature,
                name=function_name,
                modifiers='virtual',
                output=self._additional_outputs[-1]):
            if not uses_rpc_options:
                self._generate_client_nonrpcoptions_function(service,
                        function, function_name)
            else:
                common_args = []
                for arg in function.arglist.members:
                    common_args.append(arg.name)

                promise_name = self.tmp("promise")

                return_type = _lift_unit(self._type_name(function.returntype))

                if header:
                    out("folly::Promise<std::pair<{type}, std::unique_ptr<apache::thrift::transport::THeader>>> {promise};"
                            .format(type=return_type, promise=promise_name))
                else:
                    out("folly::Promise<{type}> {promise};"
                            .format(type=return_type, promise=promise_name))

                future_name = self.tmp("future")
                out("auto {future} = {promise}.getFuture();"
                  .format(future=future_name, promise=promise_name))

                args = ["rpcOptions"]
                end_args = []

                callback = self.tmp("callback")

                if function.oneway:
                    out("auto {callback} = "
                        "folly::make_unique<apache::thrift::OneWayFutureCallback>("
                        "std::move({promise}), channel_);"
                        .format(callback=callback,
                                promise=promise_name))

                    args.append("std::move({0})".format(callback))

                else:
                    if header:
                        future_cb_name = "HeaderFutureCallback"
                    else:
                        future_cb_name = "FutureCallback"
                    out("auto {callback} = "
                        "folly::make_unique<apache::thrift::{future_cb}<{type}>>("
                        "std::move({promise}), recv_wrapped_{name}, channel_);"
                        .format(callback=callback,
                                future_cb=future_cb_name,
                                type=return_type,
                                promise=promise_name,
                                name=function.name))

                    args.append("std::move({0})".format(callback))

                args.extend(common_args)
                args.extend(end_args)
                args_list = ", ".join(args)

                out("{name}({args_list});".format(name=function.name,
                                                args_list=args_list))

                out("return {0};".format(future_name))

    def _generate_client_streaming_function(self, service, function,
                                            uses_rpc_options=False):

        function_name = "observable_" + function.name
        signature = self._get_streaming_function_signature(function,
                                                           uses_rpc_options)
        with out().defn(signature, name=function_name,
                    modifiers='virtual',
                    output=self._additional_outputs[-1]):
            if not uses_rpc_options:
                self._generate_client_nonrpcoptions_function(service,
                        function, function_name)
            else:
                args = [arg.name for arg in function.arglist.members]
                arglist = ", ".join(args)

                return_type = self._type_name(function.returntype)

                subj = self.tmp("subj")
                out("auto {subj} = std::make_shared<wangle::Subject<{type}>>();".format(
                    type=return_type, subj=subj))
                out("{name}(rpcOptions, "
                        "std::unique_ptr<apache::thrift::RequestCallback>("
                            "new apache::thrift::FunctionReplyCallback("
                            "[{subj}](apache::thrift::ClientReceiveState&& state) mutable {{ "
                        "apache::thrift::clientCallbackToObservable("
                            "state, recv_wrapped_{name}, {subj}); "
                        "}})), {args});".format(
                            name=function.name, subj=subj, args=arglist))
                out("return {subj};".format(subj=subj))

    def _get_streaming_function_signature(self, function, uses_rpc_options):
        result_type = self._type_name(function.returntype)
        return self._get_noncallback_function_signature(
                function,
                uses_rpc_options,
                "wangle::ObservablePtr",
                result_type)

    def _get_future_function_signature(self, function, uses_rpc_options, header):
        result_type = _lift_unit(self._type_name(function.returntype))
        if header:
            result_type = "std::pair<{0}, std::unique_ptr<apache::thrift::transport::THeader>>".format(result_type)
        return self._get_noncallback_function_signature(
                function, uses_rpc_options, "folly::Future", result_type)

    def _get_noncallback_function_signature(
            self, function, uses_rpc_options, ret_template, result_type):
        params = []
        if uses_rpc_options:
            params.append("apache::thrift::RpcOptions& rpcOptions")

        return_type = "{0}<{1}>".format(ret_template, result_type)

        param_list = ", ".join(params)
        param_list += self._argument_list(function.arglist,
                                          add_comma=bool(params),
                                          unique=False)

        return return_type + " {name}(" + param_list + ")"

    def _generate_client_async_function(self, service, function,
                                        uses_rpc_options=False,
                                        name_prefix=""):
        if not uses_rpc_options:
            signature = self._get_async_function_signature(function,
                                                           uses_rpc_options)
            with out().defn(signature,
                    name=name_prefix + function.name,
                    modifiers='virtual',
                    output=self._additional_outputs[-1]):
                out('::apache::thrift::RpcOptions rpcOptions;')
                args = ["rpcOptions"]

                args.append("std::move(callback)")

                args.extend([arg.name for arg in function.arglist.members])
                args_list = ", ".join(args)

                out("{name}({args});".format(name=function.name, args=args_list))

        else:
            signature = self._get_async_function_signature(
                    function, uses_rpc_options=True, uses_callback_ptr=True)

            with out().defn(signature,
                    name=name_prefix + function.name,
                    modifiers='virtual',
                    output=self._additional_outputs[-1]):
                args = ["&writer", "rpcOptions"]
                args.append("std::move(callback)")

                for arg in function.arglist.members:
                    args.append(arg.name)

                args_list = ", ".join(args)

                with out("switch(getChannel()->getProtocolId())"):
                    for key, val, prot in self.protocols:
                        if key == 'simple_json':
                            continue
                        with out().case("apache::thrift::protocol::" + prot):
                            out("apache::thrift::{0}Writer writer;".format(val))
                            out("{name}T({args});".
                              format(name=function.name, args=args_list))

                    with out().case("default", nobreak=True):
                        out("throw apache::thrift::TApplicationException("
                          '"Could not find Protocol");')

    def _generate_templated_client_function(self, service, function):
        signature = self._get_async_function_signature(function,
                                                       uses_rpc_options=True,
                                                       uses_template=True,
                                                       uses_callback_ptr=True)

        func_name = function.name + "T"

        with out().defn(signature, name=func_name, output=self._out_tcc):
            out("auto header = std::make_shared<apache::thrift::transport::THeader>(apache::thrift::transport::THeader::ALLOW_BIG_FRAMES);")
            out("header->setProtocolId(getChannel()->getProtocolId());")
            out("header->setHeaders(rpcOptions.releaseWriteHeaders());")
            out("connectionContext_->setRequestHeader(header.get());")
            out("std::unique_ptr<apache::thrift::ContextStack> ctx = "
              "this->getContextStack(this->getServiceName(), "
              '"{0}.{1}", connectionContext_.get());'
              .format(service.name, function.name))
            pargs_class = "{0}_{1}_pargs".format(service.name, function.name)
            out("{0} args;".format(pargs_class))

            # Generate list of function args
            for idx, field in enumerate(function.arglist.members):
                rtype = self._get_true_type(field.type)
                if rtype.is_string or rtype.is_container \
                        or rtype.is_struct:
                    out("{0} = const_cast<{1}*>(&{2});".format(
                            self._get_pargs_field(idx, field),
                            self._type_name(field.type),
                            field.name))
                else:
                    out("{0} = &{1};".format(
                        self._get_pargs_field(idx, field),
                        field.name))

            if self.flag_compatibility:
                sizer = '[](Protocol_* prot, {0}& args) ' \
                        '{{ return {0}_serializedSizeZC(prot, &args); }}'
                writer = '[](Protocol_* prot, {0}& args) ' \
                         '{{ {0}_write(prot, &args); }}'
            else:
                sizer = '[](Protocol_* prot, {0}& args) ' \
                        '{{ return args.serializedSizeZC(prot); }}'
                writer = '[](Protocol_* prot, {0}& args) ' \
                         '{{ args.write(prot); }}'
            sizer = sizer.format(pargs_class)
            writer = writer.format(pargs_class)

            out('apache::thrift::clientSendT<{}>(prot, rpcOptions, '
                'std::move(callback), std::move(ctx), header, '
                'channel_.get(), args, '
                '"{}", {}, {});'.format(["false", "true"][function.oneway],
                                      function.name, writer, sizer))
            out("connectionContext_->setRequestHeader(nullptr);")

    def _get_async_function_signature(self,
                                      function,
                                      uses_rpc_options,
                                      uses_template=False,
                                      uses_callback_ptr=False):
        signature_prefix = ""
        if uses_template:
            signature_prefix = "template <typename Protocol_>\n"

        params = []
        if uses_template:
            params.append("Protocol_* prot")

        if uses_rpc_options:
            params.append("apache::thrift::RpcOptions& rpcOptions")

        params.append("std::unique_ptr<apache::thrift::RequestCallback> "
                      "callback")

        param_list = ", ".join(params)

        param_list += self._argument_list(function.arglist,
                                          add_comma=bool(params),
                                          unique=False)

        return signature_prefix + "void {name}(" + param_list + ")"

    def _generate_client_std_function(self, function, name_prefix=""):
        sig = ("void {name}(std::function<void ("
               "::apache::thrift::ClientReceiveState&&)> callback" +
               self._argument_list(function.arglist, True, unique=False) + ")")

        args = ["folly::make_unique<apache::thrift::FunctionReplyCallback>("
                "std::move(callback))"]
        args.extend([arg.name for arg in function.arglist.members])
        args_list = ",".join(args)

        name = name_prefix + function.name

        with out().defn(sig,
                        name=name,
                        modifiers="virtual",
                        output=self._additional_outputs[-1]):
            out("{name}({args});".format(name=function.name, args=args_list))

    def _generate_throwing_recv_function(self, function, uses_template):
        callee_name = function.name
        if uses_template:
            callee_name = callee_name + 'T'
        output = None
        if uses_template:
            output = self._out_tcc
        else:
            output = self._additional_outputs[-1]
        with out().defn(self._get_recv_function_signature(function,
                                                          uses_template),
                        name='recv_' + callee_name,
                        modifiers='static',
                        output=output):
            simple_return = not function.returntype.is_void and \
                not self._is_complex_type(function.returntype)
            if simple_return:
                out(self._type_name(function.returntype) + ' _return;')

            params = []
            if uses_template:
                params.append('prot')
            if not function.returntype.is_void:
                params.append('_return')
            params.append('state')
            func_name = 'recv_wrapped_' + function.name
            if uses_template:
                func_name = func_name + 'T'
            out('auto ew = {0}({1});'.format(func_name, ', '.join(params)))
            with out('if (ew)'):
                out('ew.throwException();')
            if simple_return:
                out('return _return;')

    def _generate_recv_functions(self, function):
        with out().defn(self._get_recv_function_signature(function,
                                                          is_wrapped=True),
                        name="recv_wrapped_" + function.name,
                        modifiers="static",
                        output=self._additional_outputs[-1]):
            out('auto ew = state.exceptionWrapper();')
            with out('if (ew)'):
                out('return ew;')
            with out('if (!state.buf())'):
                out('return folly::make_exception_wrapper<'
                    'apache::thrift::TApplicationException>('
                    '"recv_ called without result");')
            with out("switch(state.protocolId())"):
                for key, value, prottype in self.protocols:
                    if key == 'simple_json':
                        continue
                    with out().case('apache::thrift::protocol::' + prottype,
                                    nobreak=True):
                        out("apache::thrift::{0}Reader reader;".format(value))

                        callee_name = "recv_wrapped" + function.name + "T"

                        args = ["&reader"]

                        if not function.returntype.is_void:
                            args.append("_return")

                        args.append("state")
                        args_list = ", ".join(args)

                        out('return recv_wrapped_' + function.name + 'T(' +
                            args_list + ');')

                with out().case("default", nobreak=True):
                    pass
            out('return folly::make_exception_wrapper<'
                'apache::thrift::TApplicationException>('
                '"Could not find Protocol");')

        self._generate_throwing_recv_function(function, False)

        # Most mock frameworks require your functions to be virtual instance
        # functions. Generating a virtual instance version of recv_ so
        # that folks wanting to mock the thrift service clients can use
        # this function and override in their mock objects.
        out("// Mock friendly virtual instance method")
        with out().defn(self._get_recv_function_signature(function),
                    name="recv_" + "instance_" + function.name,
                    modifiers="virtual",
                    output=self._additional_outputs[-1]):
            params = []
            if self._is_complex_type(function.returntype):
                params.append('_return')
            params.append('state')
            if not function.oneway:
                if not function.returntype.is_void:
                    out("return recv_" + function.name +
                        "(" + ", ".join(params) + ");")
                else:
                    out("recv_" + function.name + "(" + ", ".join(params) +
                        ");")

        with out().defn(self._get_recv_function_signature(function,
                                                          is_wrapped=True),
                    name="recv_instance_wrapped_" + function.name,
                    modifiers="virtual",
                    output=self._additional_outputs[-1]):
            params = []
            if not function.returntype.is_void:
                params.append('_return')
            params.append('state')
            if not function.oneway:
                out("return recv_wrapped_" + function.name +
                    "(" + ", ".join(params) + ");")

    def _generate_templated_recv_function(self, service, function):
        sig = self._get_recv_function_signature(function,
                                                uses_template=True,
                                                is_wrapped=True)

        with out().defn(sig,
                    name="recv_wrapped_" + function.name + "T",
                    modifiers="static",
                    output=self._out_tcc):
            with out('if (state.isException())'):
                out('return state.exceptionWrapper();')
            out("prot->setInput(state.buf());")
            out("auto guard = folly::makeGuard([&] {prot->setInput(nullptr);});")
            out("apache::thrift::ContextStack* ctx = state.ctx();")
            out("std::string fname;")
            out("int32_t protoSeqId = 0;")
            out("apache::thrift::MessageType mtype;")
            out("ctx->preRead();")

            out("folly::exception_wrapper interior_ew;")
            with out("auto caught_ew = folly::try_and_catch<"
                     "apache::thrift::TException, "
                     "apache::thrift::protocol::TProtocolException>([&]() "):
                out("prot->readMessageBegin(fname, mtype, protoSeqId);")

                with out("if (mtype == apache::thrift::T_EXCEPTION)"):
                    out("apache::thrift::TApplicationException x;")
                    out("x.read(prot);")
                    out("prot->readMessageEnd();")
                    out("interior_ew = folly::make_exception_wrapper<"
                        "apache::thrift::TApplicationException>(x);")
                    out("return; // from try_and_catch")

                with out("if (mtype != apache::thrift::T_REPLY)"):
                    out("prot->skip(apache::thrift::protocol::T_STRUCT);")
                    out("prot->readMessageEnd();")
                    out("interior_ew = folly::make_exception_wrapper<"
                        "apache::thrift::TApplicationException>("
                        "apache::thrift::TApplicationException"
                        "::TApplicationExceptionType::INVALID_MESSAGE_TYPE);")
                    out("return; // from try_and_catch")

                with out('if (fname.compare("' + function.name + '") != 0)'):
                    out("prot->skip(apache::thrift::protocol::T_STRUCT);")
                    out("prot->readMessageEnd();")
                    out("interior_ew = folly::make_exception_wrapper<"
                        "apache::thrift::TApplicationException>("
                        "apache::thrift::TApplicationException"
                        "::TApplicationExceptionType::WRONG_METHOD_NAME);")
                    out("return; // from try_and_catch")
                out("::apache::thrift::SerializedMessage smsg;")
                out("smsg.protocolType = prot->protocolType();")
                out("smsg.buffer = state.buf();")
                out("ctx->onReadData(smsg);")

                out("{0}_{1}_presult result;".format(service.name, function.name))

                if not function.returntype.is_void:
                    out("{0} = &_return;".format(
                        self._get_presult_success()))

                if self.flag_compatibility:
                    out("{0}_{1}_presult_read(prot, &result);".format(
                            service.name, function.name))
                else:
                    out("result.read(prot);")

                out("prot->readMessageEnd();")
                out('ctx->postRead(state.header(), state.buf()->length());')

                if not function.returntype.is_void:
                    with out("if ({0})".format(self._get_presult_success_isset())):
                        out("// _return pointer has been filled")
                        out("return; // from try_and_catch")
                for idx, xs in enumerate(function.xceptions.members):
                    ex_idx = self._exception_idx(function, idx)
                    with out("if ({0})".format(
                            self._get_presult_exception_isset(ex_idx, xs))):
                        out("interior_ew = folly::make_exception_wrapper<"
                                "{0}>({1});".format(
                                self._type_name(xs.type),
                                self._get_presult_exception(ex_idx, xs)))
                        out("return; // from try_and_catch")

                if not function.returntype.is_void:
                    with out("else"):
                        # else throw, no success
                        out("interior_ew = folly::make_exception_wrapper<"
                            "apache::thrift::TApplicationException>("
                            "apache::thrift::TApplicationException::"
                            "TApplicationExceptionType::MISSING_RESULT, "
                            '"failed: unknown result");')
                        out("return; // from try_and_catch")
            out(");")
            out("auto ew = interior_ew ? std::move(interior_ew) : std::move(caught_ew);")
            with out("if (ew)"):
                out("ctx->handlerErrorWrapped(ew);")
            out("return ew;")

        self._generate_throwing_recv_function(function, True)

    def _get_recv_function_signature(self, function, uses_template=False,
                                     is_wrapped=False):
        signature_prefix = ""

        if uses_template:
            signature_prefix = "template <typename Protocol_>\n"

        if is_wrapped:
            signature_prefix += 'folly::exception_wrapper'
        elif function.returntype.is_void or \
           self._is_complex_type(function.returntype):
            signature_prefix += "void"
        else:
            signature_prefix += self._type_name(function.returntype)

        params = []
        if uses_template:
            params.append("Protocol_* prot")

        if not function.returntype.is_void and (
            self._is_complex_type(function.returntype) or is_wrapped
        ):
            params.append(self._type_name(function.returntype) + "& _return")

        params.append("::apache::thrift::ClientReceiveState& state")
        param_list = ", ".join(params)
        return signature_prefix + " {name}(" + param_list + ")"

    def _recv_has_void_return_type(self, function):
        return not self._function_produces_result(function) or \
               self._recv_uses_return_parameter(function)

    def _recv_uses_return_parameter(self, function):
        return self._function_produces_result(function) and \
               self._is_complex_type(function.returntype)

    def _function_produces_result(self, function):
        return_type = function.returntype
        return not return_type.is_void

    def _exception_idx(self, function, idx):
        return idx + (1 if self._function_produces_result(function) else 0)

    def _argument_list(self, arglist, add_comma, unique, no_param_names=False):
        out = ""
        for field in arglist.members:
            if len(out) > 0 or add_comma:
                out += ", "

            type_name = self._type_name(field.type,
                                        arg=True, unique=unique)

            field_name = "/*" + field.name + "*/" if no_param_names \
                         else field.name
            out += type_name + " " + field_name

        return out

    # ======================================================================
    # STRUCT GENERATION CODE + following two sections
    # ======================================================================

    def _default_value(self, t):
        t = self._get_true_type(t)
        if t.is_base_type or t.is_enum:
            dval = None
            if t.is_enum:
                dval = "({0})0".format(self._type_name(t))
            elif t.is_string:
                dval = '{0}()'.format(self._type_name(t))
            else:
                dval = '0'
            return dval
        else:
            return False

    def _should_generate_field(self, field):
        return ('format' not in field.annotations or
                field.annotations['format'] != 'serialized')

    def _member_default_value(self, member, explicit=False):
        t = self._get_true_type(member.type)
        if member.value:
            return self._render_const_value(t, member.value)
        if self._is_optional_wrapped(member):
            return ''
        if t.is_base_type and not t.is_string:
            return '0'
        if explicit or t.is_enum:
            return '{0}()'.format(self._type_name(t))
        return ''

    def _get_serialized_fields_options(self, obj):
        keep_unknown_fields = ('keep_unknown_fields' in obj.annotations and
                               obj.annotations['keep_unknown_fields'] == '1')
        return SerializedFieldOptions(
            keep_unknown_fields=keep_unknown_fields,
            has_serialized_fields=keep_unknown_fields or any(
                    'format' in field.annotations and
                    field.annotations['format'] == 'serialized'
                    for field in obj.members)
        )

    def _gen_union_constructor(self, s, obj, is_operator, is_move):
        i = OrderedDict()
        if is_operator:
            if is_move:
                sig = '{name}& operator=({name}&& rhs)'
            else:
                sig = '{name}& operator=(const {name}& rhs)'
        else:
            i['type_'] = 'Type::__EMPTY__'
            if is_move:
                sig = '{name}({name}&& rhs)'
            else:
                sig = '{name}(const {name}& rhs)'

        with s.defn(sig, name=obj.name, in_header=True, init_dict=i):
            out('if (this == &rhs) {{return{0}; }}'.format(
                ' *this' if is_operator else ''))
            if is_operator:
                out('__clear();')
            out('if (rhs.type_ == Type::__EMPTY__) {{ return{0}; }}'.format(
                ' *this' if is_operator else ''))

            with out('switch(rhs.type_)'):
                for member in obj.members:
                    with out().case('Type::' + member.name):
                        if is_move:
                            if self._is_reference(member):
                                fmt = 'set_{field}(std::move(*rhs.value_.{field}));'
                            else:
                                fmt = 'set_{field}(std::move(rhs.value_.{field}));'
                        elif self._is_reference(member):
                            fmt = 'set_{field}(*rhs.value_.{field});'
                        else:
                            fmt = 'set_{field}(rhs.value_.{field});'
                        out(fmt.format(field=member.name))
                with out().case('default'):
                    out('assert(false);')

            if is_move:
                out('rhs.__clear();')
            if is_operator:
                out('return *this;')

    def _gen_union_switch(self, members, stmt,
                          val='type_', default='assert(false);'):
        with out('switch({0})'.format(val)):
            for member in members:
                with out().case('Type::' + member.name):
                    out(stmt.format(field=member.name))
            with out().case('default'):
                out(default)

    def _generate_struct_complete(self, s, obj, is_exception,
                                  pointers, read, write, swap,
                                  result, has_isset=True,
                                  to_additional=False, simplejson=True):
        def output(data):
            if to_additional:
                print >>self._additional_outputs[-1], data
            else:
                s.impl(data)

        if not self.flag_implicit_templates:
            for a,b,c in self.protocols:
                if a == 'simple_json' and not simplejson:
                    continue
                if not self.flag_compatibility:
                    output(("template uint32_t {1}::read<apache::thrift::{0}Reader>"
                           "(apache::thrift::{0}Reader*);").format(b,obj.name))

                    output(("template uint32_t {1}::write<"
                            "apache::thrift::{0}Writer"">("
                            "apache::thrift::{0}Writer*) const;").format(
                                    b,obj.name))
                    output(("template uint32_t {1}::serializedSize"
                           "<apache::thrift::{0}Writer>(apache::thrift::{0}Writer*)"
                            " const;").format(b,obj.name))
                    output(("template uint32_t {1}::serializedSizeZC"
                            "<apache::thrift::{0}Writer>("
                            "apache::thrift::{0}Writer*) const;").format(
                                        b,obj.name))
                else:
                    output(("template uint32_t {1}_read<"
                            "apache::thrift::{0}Reader>("
                            "apache::thrift::{0}Reader*, {1}*);").format(
                                        b,obj.name))
                    output(("template uint32_t {1}_write<"
                            "apache::thrift::{0}Writer>("
                            "apache::thrift::{0}Writer*, const {1}*);").format(
                                    b,obj.name))
                    output(("template uint32_t {1}_serializedSize<"
                            "apache::thrift::{0}Writer>("
                            "apache::thrift::{0}Writer*, const {1}*);").format(
                                    b,obj.name))
                    output(("template uint32_t {1}_serializedSizeZC<"
                            "apache::thrift::{0}Writer>("
                            "apache::thrift::{0}Writer*, const {1}*);").format(
                                        b,obj.name))

        if self.flag_compatibility:
            base = self._namespace_prefix(
                    self._program.get_namespace('cpp')) + obj.name
            s('typedef{0} {1};'.format(base, obj.name))

            if read:
                self._generate_struct_reader(s, obj, pointers,
                                             has_isset=has_isset)
            if write:
                for zc in False, True:
                    self._generate_struct_compute_length(
                            s, obj, pointers, result, zero_copy=zc)
                self._generate_struct_writer(s, obj, pointers, result)
            return

        extends = ' : private boost::totally_ordered<{0}>'.format(obj.name)
        if is_exception:
            extends += ', public apache::thrift::TException'
        # Open struct def
        struct = s.cls('class {0}{1}'.format(obj.name, extends)).scope
        struct.acquire()
        struct.label('public:')
        # Get members
        members = filter(self._should_generate_field, obj.members)
        has_nonrequired_fields = any(member.req != e_req.required
                                        for member in members)
        should_generate_isset = has_nonrequired_fields and \
            ((not pointers) or read) and not obj.is_union \
            and not self.flag_optionals
        struct_options = self._get_serialized_fields_options(obj)

        # Type enum for unions
        if obj.is_union:
            with struct('enum Type'):
                out('__EMPTY__ = 0,')
                i = 0
                for member in members:
                    i += 1
                    out('{0} = {1},'.format(member.name, i))
            struct.sameLine(';')

        if not pointers:
            # Default constructor
            i = OrderedDict()
            if not obj.is_union:
                for member in members:
                    value = self._member_default_value(member)
                    if value:
                        i[member.name] = value
                struct()
            else:
                i['type_'] = 'Type::__EMPTY__'
            c = struct.defn('{name}()', name=obj.name,
                                    in_header=True, init_dict=i).scope.empty()

            # generate from-string ctors for exceptions with message annotation
            if is_exception and 'message' in obj.annotations:
                i = OrderedDict()
                i[obj.annotations['message']] = '__message'
                c = struct.defn('explicit {name}(const std::string& __message)',
                                name=obj.name,
                                in_header=True,
                                init_dict=i).scope.empty()

                i = OrderedDict()
                i[obj.annotations['message']] = 'std::move(__message)'
                c = struct.defn('explicit {name}(std::string&& __message)',
                                name=obj.name,
                                in_header=True,
                                init_dict=i).scope.empty()

            is_copyable = self._is_copyable_struct(obj)
            is_comparable = self._is_comparable_struct(obj)
            if not obj.is_union:
                # Generate a initializer_list type constructor
                init_vars = []
                init_vars.append('apache::thrift::FragileConstructor')
                for member in members:
                    t = self._get_true_type(member.type)
                    typename = self._type_name(member.type)
                    if self._is_reference(member):
                        typename = "std::unique_ptr<" + typename + ">"
                    init_vars.append("{0} {1}__arg".format(
                        typename, member.name))
                i = OrderedDict()
                for member in members:
                    i[member.name] = 'std::move({name}__arg)'.format(
                        name=member.name)
                struct('// FragileConstructor for use in'
                       ' initialization lists only')
                c = struct.defn('{name}(' + ', '.join(init_vars) + ')',
                                name=obj.name,
                                in_header=True,
                                init_dict=i).scope.empty()

                # move constructor, move assignment, defaulted
                # (not implicitly declared because we have a destructor)
                if self._is_noex_move_ctor_struct(obj):
                    i = OrderedDict()
                    for member in members:
                        i[member.name] = 'std::move(other.{name})'.format(
                            name=member.name)
                    if should_generate_isset:
                        i['__isset'] = 'other.__isset'
                    c = struct.defn('{name}({name}&& other)',
                                    name=obj.name,
                                    in_header=True,
                                    no_except=True,
                                    init_dict=i).scope.empty()
                else:
                    c = struct.defn('{name}({name}&&)',
                                    name=obj.name, in_header=True, default=True)
                if is_copyable:
                    needs_copy_constructor = False
                    for member in members:
                        if self._is_reference(member):
                            needs_copy_constructor = True
                    if needs_copy_constructor:
                        src = self.tmp('src')
                        with struct.defn('{{name}}(const {0}& {1})'
                                         .format(obj.name, src),
                                         name=obj.name):
                            for member in members:
                                if self._is_reference(member):
                                    out("if ({2}.{0}) {0}.reset("
                                        "new {1}(*{2}.{0}));".format(
                                        member.name, self._type_name(
                                            member.type), src))
                                else:
                                    out("{0} = {1}.{0};".format(
                                        member.name, src))
                                if self._has_isset(member):
                                    out('__isset.{0} = {1}.__isset.{0};'.format(
                                        member.name, src))
                    else:
                        c = struct.defn(
                            '{name}(const {name}&)',
                            name=obj.name, in_header=True, default=True)
                c = struct.defn('{name}& operator=({name}&&)',
                                name=obj.name, in_header=True, default=True)
                if is_copyable:
                    if needs_copy_constructor:
                        src = self.tmp('src')
                        tmp = self.tmp('tmp')
                        with struct.defn('{0}& {{name}}(const {0}& {1})'
                                         .format(obj.name, src),
                                         name='operator='):
                            out('{name} {tmp}({src});'.format(
                                name=obj.name, tmp=tmp, src=src))
                            out('swap(*this, {tmp});'.format(tmp=tmp))
                            out('return *this;')
                    else:
                        c = struct.defn('{name}& operator=(const {name}&)',
                                    name=obj.name, in_header=True, default=True)

            else:
                # unions need to define the above constructors because of the
                # union member
                for op in False, True:
                    self._gen_union_constructor(struct, obj, op, True)
                    if is_copyable:
                        self._gen_union_constructor(struct, obj, op, False)

            if len(members) > 0:
                with struct.defn('void {name}()', name="__clear"):
                    if obj.is_union:
                        out('if (type_ == Type::__EMPTY__) { return; }')
                        self._gen_union_switch(members,
                            'destruct(value_.{field});')
                        out('type_ = Type::__EMPTY__;')
                    else:
                        for member in members:
                            t = self._get_true_type(member.type)
                            name = member.name + \
                                self._type_access_suffix(member.type)
                            if self._is_optional_wrapped(member):
                                # trumps below conditions, regardless of type
                                out(('{0}.clear();').format(name))
                            elif t.is_base_type or t.is_enum:
                                dval = self._member_default_value(
                                        member, explicit=True)
                                out('{0} = {1};'.format(name, dval))
                            elif t.is_struct or t.is_xception:
                                stype = self._get_true_type(
                                    member.type.as_struct)
                                if len(stype.members) > 0:
                                    if self._is_reference(member):
                                        out(('if ({1}) ' +
                                             '::apache::thrift::Cpp2Ops< {0}>' +
                                             '::clear({1}.get());').format(
                                                 self._type_name(member.type),
                                                                 name))
                                    else:
                                        out(('::apache::thrift::Cpp2Ops< {0}>' +
                                             '::clear(&{1});').format(
                                                 self._type_name(member.type),
                                                                 name))
                            elif t.is_container:
                                out('{0}.clear();'.format(name))
                            else:
                                raise TypeError('Unknown type for member:' +
                                                member.name)
                        if should_generate_isset:
                            out('__isset.__clear();')
                        if struct_options.has_serialized_fields:
                            out('{0}.reset();'.format(
                                self._serialized_fields_name))
        # END if not pointers

        if 'final' not in obj.annotations:
            with struct.defn('~{name}() throw()', name=obj.name,
                             modifiers='virtual', in_header=True):
                if obj.is_union:
                    out('__clear();')
            struct()

        s1 = struct
        if obj.is_union:
            s1 = struct('union storage_type').scope
            s1.acquire()

        # Declare all fields.
        for member in members:
            s1(self._declare_field(
                member,
                pointers and not member.type.is_xception,
                not read, False,
                self._is_reference(member),
                self._is_optional_wrapped(member)))

        if s1 is not struct:
            s1()
            s1('storage_type() {}')
            s1('~storage_type() {}')
            s1.release()
            struct.sameLine(';')

        # Isset struct has boolean fields, but only for non-required fields
        if should_generate_isset:
            struct()
            with struct.cls('struct __isset', epilogue=' __isset;') as ist:
                with ist.defn('__isset()', in_header=True):
                    out('__clear();')
                with ist.defn('void __clear()', in_header=True):
                    for member in members:
                        if self._has_isset(member):
                            out("{0} = false;".format(member.name))
                # Declare boolean fields
                ist()
                for member in members:
                    if self._has_isset(member):
                        ist('bool {0};'.format(member.name))
        if struct_options.has_serialized_fields:
            struct()
            struct('apache::thrift::ProtocolType {0};'.format(
                       self._serialized_fields_protocol_name))
            struct('{0} {1};'.format(self._serialized_fields_type,
                                     self._serialized_fields_name))
        if ((not pointers and
             is_comparable and
             not struct_options.has_serialized_fields)):
            # Generate an equality testing operator.
            with struct.defn('bool {{name}}(const {0}& {1}) const'
                             .format(obj.name,
                                len(members) > 0 and 'rhs' or '/* rhs */'),
                             name='operator=='):
                if obj.is_union:
                    out('if (type_ != rhs.type_) { return false; }')
                    self._gen_union_switch(members,
                        'return value_.{field} == rhs.value_.{field};',
                        default='return true;')
                else:
                    for m in members:
                        # Most existing Thrift code does not use isset or
                        # optional/required, so we treat "default" fields as
                        # required.
                        if self._is_reference(m):
                            check = ("(({0} && rhs.{0} && *{0} == *rhs.{0}) ||"
                                     "(!{0} && !rhs.{0}))").format(m.name)
                        else:
                            check = "({0} == rhs.{0})".format(m.name)

                        ctype = self._get_true_type(m.type)
                        if ctype.is_base_type and ctype.as_base_type.is_binary:
                            check = "apache::thrift::StringTraits<{0}>::" \
                                "isEqual({1}, rhs.{1})".format(
                                self._type_name(ctype), m.name)
                        if m.req != e_req.optional or not self._has_isset(m):
                            with out('if (!({0}))'.format(
                                    check)):
                                out('return false;')
                        else:
                            with out('if (__isset.{0} != rhs.__isset.{0})'
                                    .format(m.name)):
                                out('return false;')
                            with out('else if'
                                    ' (__isset.{0} && !({1}))'
                                    .format(m.name, check)):
                                out('return false;')
                    out('return true;')

            # Generate the declaration of a less-than operator. This must be
            # implemented by the application developer if they wish to use it.
            # (They will get a link error if they try to use it without an
            # implementation.)i
            if self._is_orderable_type(obj) and \
              'no_default_comparators' not in obj.annotations:
                with struct.defn('bool operator < (const {0}& rhs) const'
                  .format(obj.name), in_header=True):
                    if obj.is_union:
                        out('if (type_ != rhs.type_)'
                            ' { return type_ < rhs.type_; }')
                        self._gen_union_switch(members,
                            'return value_.{field} < rhs.value_.{field};',
                            default='return false;')
                    else:
                        for m in members:
                            with out('if (!({0} == rhs.{0}))'
                                    .format(m.name)):
                                out('return {0} < rhs.{0};'.format(m.name))
                        out('return false;')
            else:
                struct('bool operator < (const {0}& rhs) const;'
                  .format(obj.name))

        # generate union accessors/settors
        if obj.is_union:
            for member in members:
                t = self._type_name(self._get_true_type(member.type))
                with struct.defn('template<typename... T>\n'
                                 'void set_{name}(T&&... t)',
                                 in_header=True, name=member.name):
                    out('__clear();')
                    out('type_ = Type::{0};'.format(member.name))
                    if self._is_reference(member):
                        out('new (&value_.{0}) std::unique_ptr<{1}>('
                                'new {1}(std::forward<T>(t)...));'.format(
                                    member.name, t))
                    else:
                        out('new (&value_.{0}) {1}(std::forward<T>(t)...);'
                                .format(member.name, t))

            for member in members:
                t = self._type_name(self._get_true_type(member.type))
                if self._is_reference(member):
                    t = "std::unique_ptr<" + t + ">"
                with struct.defn('get_{name}() const', in_header=True,
                        name=member.name,
                        modifiers='const {0}&'.format(t)):
                    out('assert(type_ == Type::{0});'.format(member.name))
                    out('return value_.{0};'.format(member.name))

            for member in members:
                t = self._type_name(self._get_true_type(member.type))
                if self._is_reference(member):
                    t = "std::unique_ptr<" + t + ">"
                with struct.defn('mutable_{name}()', in_header=True,
                        name=member.name,
                        modifiers='{0}&'.format(t)):
                    out('assert(type_ == Type::{0});'.format(member.name))
                    out('return value_.{0};'.format(member.name))

            for member in members:
                t = self._type_name(self._get_true_type(member.type))
                if self._is_reference(member):
                    t = "std::unique_ptr<" + t + ">"
                with struct.defn('move_{name}()', in_header=True,
                        name=member.name, modifiers=t):
                    out('assert(type_ == Type::{0});'.format(member.name))
                    out('return std::move(value_.{0});'.format(member.name))

            struct()
            struct('Type getType() const { return type_; }')

        if read or write:
            struct()
        if read:
            self._generate_struct_reader(struct, obj, pointers)
        if write:
            for zc in False, True:
                self._generate_struct_compute_length(
                        struct, obj, pointers, result, zero_copy=zc)
            self._generate_struct_writer(struct, obj, pointers, result)
        if is_exception:
            if 'message' in obj.annotations:
                what = '{0}.c_str()'.format(obj.annotations['message'])
            else:
                what = '"{0}"'.format(self._type_name(obj))
            with struct.defn('virtual const char* what() const throw()',
                                   in_header=True) as x1:
                x1('return {0};'.format(what))

        # generate the union protected members
        if obj.is_union:
            struct.label('protected:')
            with struct.defn('template <class T>\n' + 'void destruct(T &val)',
                            in_header=True):
                out('(&val)->~T();')
            struct()
            struct('Type type_;')
            struct('storage_type value_;')

        if self._has_cpp_annotation(obj, 'methods'):
            struct('// user defined code (cpp2.methods = ...)')
            struct(self._cpp_annotation(obj, 'methods'))

        # we're done with the struct definition
        struct.release()

        if swap:
            s = self._types_scope
            # Generate a namespace-scope swap() function
            with s.defn('void {{name}}({0}& a, {0}& b)'.format(obj.name),
                        name='swap'):
                if obj.is_union:
                    # For unions, the members cannot be swapped individually
                    # so instead we use the logic in the move constructors to
                    # swap the object wholesale
                    out('{0} temp(std::move(a));'.format(obj.name))
                    out('a = std::move(b);')
                    out('b = std::move(temp);')
                else:
                    # Let argument-dependent name lookup find the correct swap()
                    # function to use based on the argument types. If none is found
                    # in the arguments' namespaces, fall back to ::std::swap().
                    out('using ::std::swap;')
                    has_nonrequired_fields = False
                    for tfield in members:
                        ttype = self._get_true_type(tfield.type)
                        if tfield.req != e_req.required:
                            has_nonrequired_fields = True
                        out('swap(a.{0}, b.{0});'.format(tfield.name))
                    if should_generate_isset:
                        out('swap(a.__isset, b.__isset);')
                    if struct_options.has_serialized_fields:
                        out('swap(a.{0}, b.{0});'.format(
                                self._serialized_fields_protocol_name))
                        out('swap(a.{0}, b.{0});'.format(
                                self._serialized_fields_name))

    # ======================================================================
    # DESERIALIZATION CODE
    # ======================================================================

    def _generate_struct_reader(self, scope, obj,
                                pointers=False, has_isset=True):
        if self.flag_optionals:
            has_isset = False
        this = 'this'
        if self.flag_compatibility:
            this = 'obj'
            name = '{0}_read'.format(obj.name)
            d = scope.defn('template <class Protocol_>\n' +
                           'uint32_t {name}(Protocol_* iprot, ' + obj.name +
                           '* obj)',
                           name=name,
                           output=self._out_tcc)
        else:
            d = scope.defn('template <class Protocol_>\n'
                           'uint32_t {name}(Protocol_* iprot)', name='read',
                           output=self._out_tcc)
        if obj.is_union:
            has_isset = False
        fields = obj.members

        struct_options = self._get_serialized_fields_options(obj)
        # s = scope of the read() function
        s = d.scope
        # Declare stack tmp variables
        s('uint32_t xfer = 0;')
        s('std::string fname;')
        s('apache::thrift::protocol::TType ftype;')
        s('int16_t fid;')
        s()
        s('xfer += iprot->readStructBegin(fname);')
        s()
        s('using apache::thrift::TProtocolException;')
        s()
        # Special handling for serialized fields
        if struct_options.has_serialized_fields:
            s('{0}->{1} = iprot->protocolType();'.format(
                this, self._serialized_fields_protocol_name))
            s('std::unique_ptr<folly::IOBuf> serialized;')
        # Required variables aren't in __isset, so we need tmp vars to
        # check them.
        req_fields = ifilter(lambda field: field.req == e_req.required,
                             fields)
        for field in req_fields:
            s('bool isset_{0.name} = false;'.format(field))
        s()

        fields_scope = None
        if obj.is_union:
            # Unions only have one member set, so don't loop
            s('xfer += iprot->readFieldBegin(fname, ftype, fid);')
            s1 = s('if (ftype == apache::thrift::protocol::T_STOP)').scope
            s1(this + '->__clear();')
            s1.release()
            fields_scope = s1 = s.sameLine('else').scope
        else:
            # Loop over reading in fields
            s1 = s('while (true)').scope
            # Save the position before the field beginning
            if struct_options.has_serialized_fields:
                s1('auto fbegin = iprot->getCurrentPosition();')
                s1('bool fserialized = false;')
            # Read beginning field marker
            s1('xfer += iprot->readFieldBegin(fname, ftype, fid);')
            # Check for field STOP marker
            with s1('if (ftype == apache::thrift::protocol::T_STOP)'):
                out('break;')
            fields_scope = s

        with s1('if (fid == std::numeric_limits<int16_t>::min())'):
            cond_type = 'if'
            for field in fields:
                with s1('{0} (fname == "{1}")'.format(cond_type, field.name)):
                    s1('fid = {0};'.format(field.key))
                    s1('ftype = {0};'.format(self._type_to_enum(field.type)))
                    cond_type = 'else if'

        # Switch statement on the field we are reading
        s2 = fields_scope('switch (fid)').scope
        # Generate deserialization code for known cases
        for field in fields:
            s3 = s2.case(field.key).scope
            if ('format' in field.annotations and
                    field.annotations['format'] == 'serialized'):
                s3('fserialized = true;')
                s3('xfer += iprot->skip(ftype);')
                s3.release()  # "break;"
                continue
            s4 = s3('if (ftype == {0})'.format(self._type_to_enum(
                    field.type))).scope
            with s4:
                field_prefix = this + '->'
                field_suffix = ''
                if obj.is_union:
                    s4(field_prefix + 'set_{0}();'.format(field.name))
                    field_prefix += 'mutable_'
                    field_suffix = '()'
                if pointers and not field.type.is_xception:
                    # This is only used for read pargs, so a const-cast is okay
                    # since the struct is exposed in generated code only.
                    self._generate_deserialize_field(
                        s4, field,
                        '(*const_cast<{0}*>('.format(
                                self._type_name(field.type)) +
                                                field_prefix,
                                                field_suffix + '))')
                else:
                    self._generate_deserialize_field(s4, field, field_prefix,
                                                     field_suffix)
                if has_isset and self._has_isset(field):
                    s4('{0}->__isset.{1} = true;'.format(this, field.name))
                elif field.req == e_req.required:
                    s4('isset_{1} = true;'.format(this, field.name))
            with s3.sameLine('else'):
                out('xfer += iprot->skip(ftype);')
                # TODO(dreiss): Make this an option when thrift structs have a
                # common base class.
                # s4('throw TProtocolException(TProtocolException::'
                #    'INVALID_DATA);')
            s3.release()  # "break;"
        # Default case
        with s2.case('default'):
            out('xfer += iprot->skip(ftype);')
            if struct_options.keep_unknown_fields:
                out('fserialized = true;')
        s2.release()  # switch
        # Read field end marker
        s1('xfer += iprot->readFieldEnd();')
        # Eat the stop byte that terminates union content
        if obj.is_union:
            s1('xfer += iprot->readFieldBegin(fname, ftype, fid);')
            s1('xfer += iprot->readFieldEnd();')
        if struct_options.has_serialized_fields:
            with s1('if (fserialized)').scope:
                out('iprot->readFromPositionAndAppend(fbegin, serialized);')
        s1.release()  # while(true)
        s('xfer += iprot->readStructEnd();')
        # Finalize serialized fields buffer if necessary
        if struct_options.has_serialized_fields:
            s()
            # Thrift is supposed to be called only with IOBuf that manage
            # underlying buffer. Thus it's safe to store IOBuf pointing to the
            # parts of original buffer inside the deserialized struct.
            # Note: it might be somewhat memory inefficient as we might be
            # pointing to a small chunk of the big buffer while keeping the
            # entire buffer around. However, the current implementation leaves
            # it to the application to call coalesce() on __serialized field
            # if necessary.
            with s('if (serialized)'):
                out(('{0}->{1} = std::move(serialized);').format(
                       this, self._serialized_fields_name))
        # Throw if any required fields are missing.
        # We do this after reading the struct end so that there might possibly
        # be a chance of continuing.
        s()
        for field in filter(self._should_generate_field, fields):
            if not field.req == e_req.required:
                continue
            with s('if (!isset_{0.name})'.format(field)):
                out(('throw TProtocolException(TProtocolException::'
                'MISSING_REQUIRED_FIELD, "Required field \'{0}\' was not found'
                'in serialized data! Struct: {1}");').format(
                        field.name, obj.name))
        s('return xfer;')
        s.release()  # the function

    def _generate_deserialize_field(self, scope, field, prefix='', suffix=''):
        'Deserializes a field of any type.'
        name = prefix + field.name + self._type_access_suffix(field.type) + \
                suffix
        self._generate_deserialize_type(
            scope, field.type, name, self._is_reference(field),
            self._is_optional_wrapped(field))

    def _generate_deserialize_type(self, scope, otype, name,
                                   pointer=False, optional_wrapped=False):
        'Deserializes a variable of any type.'
        ttype = self._get_true_type(otype)
        s = scope
        if ttype.is_void:
            raise TypeError('CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: '\
                            + name)

        if ttype.is_struct or ttype.is_xception:
            self._generate_deserialize_struct(
                scope, otype, ttype.as_struct, name, pointer, optional_wrapped)
        elif ttype.is_container:
            self._generate_deserialize_container(scope, ttype.as_container,
                                                 name, otype, optional_wrapped)
        elif ttype.is_base_type:
            if optional_wrapped:
                # assign a new default-constructed value into the Optional
                s('{0} = {1}();'.format(name, self._type_name(ttype)))
            btype = ttype.as_base_type
            base = btype.base
            if base == t_base.void:
                raise CompilerError("Cannot deserialize void field in a "
                    "struct: " + name)
            elif base == t_base.string:
                if btype.is_binary:
                    txt = 'readBinary({0})'
                else:
                    txt = 'readString({0})'
            elif base == t_base.bool:
                txt = 'readBool({0})'
            elif base == t_base.byte:
                txt = 'readByte({0})'
            elif base == t_base.i16:
                txt = 'readI16({0})'
            elif base == t_base.i32:
                txt = 'readI32({0})'
            elif base == t_base.i64:
                txt = 'readI64({0})'
            elif base == t_base.double:
                txt = 'readDouble({0})'
            elif base == t_base.float:
                txt = 'readFloat({0})'
            else:
                raise CompilerError('No C++ reader for base type ' + \
                        btype.t_base_name(base) + name)
            dest = name
            if optional_wrapped:
                dest += ".value()"
            txt = 'xfer += iprot->{0};'.format(txt.format(dest))
            s(txt)
        elif ttype.is_enum:
            t = self.tmp('ecast')
            s('int32_t {0};'.format(t))
            s('xfer += iprot->readI32({0});'.format(t))
            s('{0} = ({1}){2};'.format(name, self._type_name(ttype), t))
        else:
            raise TypeError(("DO NOT KNOW HOW TO DESERIALIZE '{0}' "
                             "TYPE {1}").format(name, self._type_name(ttype)))

    def _generate_deserialize_struct(self, scope, otype, struct, prefix,
                                     pointer=False, optional_wrapped=False):
        if pointer:
            scope("{0} = std::unique_ptr<{1}>(new {1});".format(
                prefix, self._type_name(otype)))
            scope('xfer += ::apache::thrift::Cpp2Ops< {0}>::read('
                  'iprot, {1}.get());'.format(
                      self._type_name(otype), prefix))
        elif optional_wrapped:
            scope("{0} = {1}();".format(
                prefix, self._type_name(otype)))
            scope('xfer += ::apache::thrift::Cpp2Ops< {0}>::read('
                  'iprot, &{1}.value());'.format(
                      self._type_name(otype), prefix))
        else:
            scope('xfer += ::apache::thrift::Cpp2Ops< {0}>::read('
                  'iprot, &{1});'.format(
                      self._type_name(otype), prefix))

    def _generate_deserialize_container(self, scope, cont, prefix,
                                        otype, optional_wrapped):
        s = scope
        size = self.tmp('_size')
        ktype = self.tmp('_ktype')
        vtype = self.tmp('_vtype')
        etype = self.tmp('_etype')
        cpptype = self._cpp_type_name(cont)
        use_push = (cpptype is not None and 'list' in cpptype) \
            or self._has_cpp_annotation(cont, 'template')

        s('{0} = {1}();'.format(prefix, self._type_name(otype)))
        if optional_wrapped:
            cont_ref = "{0}.value()".format(prefix)
        else:
            cont_ref = prefix

        s('uint32_t {0};'.format(size))
        # Declare variables, read header
        if cont.is_map:
            s('apache::thrift::protocol::TType {0};'.format(ktype))
            s('apache::thrift::protocol::TType {0};'.format(vtype))
            s('xfer += iprot->readMapBegin({0}, {1}, {2});'.format(
                ktype, vtype, size))
        elif cont.is_set:
            s('apache::thrift::protocol::TType {0};'.format(etype))
            s('xfer += iprot->readSetBegin({0}, {1});'.format(etype, size))
        elif cont.is_list:
            s('apache::thrift::protocol::TType {0};'.format(etype))
            txt = 'xfer += iprot->readListBegin({0}, {1});'.format(etype, size)
            s(txt)
        # For loop iterates over elements
        i = self.tmp('_i')
        s('uint32_t {0};'.format(i))

        with s('if ({0} == {1})'
               .format(size, 'std::numeric_limits<uint32_t>::max()')):
            peek = 'false'
            if cont.is_map:
                peek = 'iprot->peekMap()'
            elif cont.is_set:
                peek = 'iprot->peekSet()'
            elif cont.is_list:
                peek = 'iprot->peekList()'

            with s('for ({0} = 0; {1}; {0}++)'.format(i, peek)):
                if cont.is_map:
                    self._generate_deserialize_map_element(
                            out(), cont.as_map, cont_ref)
                elif cont.is_set:
                    self._generate_deserialize_set_element(
                            out(), cont.as_set, cont_ref)
                elif cont.is_list:
                    if not use_push:
                        s('{0}.resize({1} + 1);'.format(cont_ref, i))
                    self._generate_deserialize_list_element(out(), cont.as_list,
                                                            cont_ref, use_push,
                                                            i)
        with s('else'):
            if cont.is_list and not use_push:
                s('{0}.resize({1});'.format(cont_ref, size))
            elif ((cont.is_map and cont.as_map.is_unordered) or
                  (cont.is_set and cont.as_set.is_unordered)):
                s('{0}.reserve({1});'.format(cont_ref, size))
            with s('for ({0} = 0; {0} < {1}; ++{0})'.format(i, size)):
                if cont.is_map:
                    self._generate_deserialize_map_element(
                            out(), cont.as_map, cont_ref)
                elif cont.is_set:
                    self._generate_deserialize_set_element(
                            out(), cont.as_set, cont_ref)
                elif cont.is_list:
                    self._generate_deserialize_list_element(out(), cont.as_list,
                                                            cont_ref, use_push,
                                                            i)
        # Read container end
        if cont.is_map:
            s('xfer += iprot->readMapEnd();')
        elif cont.is_set:
            s('xfer += iprot->readSetEnd();')
        elif cont.is_list:
            s('xfer += iprot->readListEnd();')

    def _generate_deserialize_map_element(self, scope, tmap, prefix):
        'Generates code to deserialize a map'
        key = self.tmp('_key')
        val = self.tmp('_val')
        fkey = frontend.t_field(tmap.key_type, key)
        fval = frontend.t_field(tmap.value_type, val)
        scope(self._declare_field(fkey))
        self._generate_deserialize_field(scope, fkey)
        scope('{0} = {1}[std::move({2})];'.format(
            self._declare_field(fval, reference=True), prefix, key))
        self._generate_deserialize_field(scope, fval)

    def _generate_deserialize_set_element(self, scope, tset, prefix):
        elem = self.tmp('_elem')
        felem = frontend.t_field(tset.elem_type, elem)
        scope(self._declare_field(felem))
        self._generate_deserialize_field(scope, felem)
        scope('{0}.insert(std::move({1}));'.format(prefix, elem))

    def _generate_deserialize_list_element(self, scope, tlist, prefix,
                                           use_push, index):
        if use_push:
            elem = self.tmp('_elem')
            felem = frontend.t_field(tlist.elem_type, elem)
            scope(self._declare_field(felem))
            self._generate_deserialize_field(scope, felem)
            scope('{0}.push_back(std::move({1}));'.format(prefix, elem))
        else:
            felem = frontend.t_field(tlist.elem_type, '{0}[{1}]'.format(prefix,
                                                                        index))
            self._generate_deserialize_field(scope, felem)

    # ======================================================================
    # SERIALIZATION CODE
    # ======================================================================

    def _generate_struct_compute_length(self, scope, obj,
                                        pointers=False,
                                        result=False,
                                        zero_copy=False):
        method = "serializedSizeZC" if zero_copy else "serializedSize"
        this = 'this'
        if self.flag_compatibility:
            this = 'obj'
            name = '{0}_{1}'.format(obj.name, method)
            d = scope.defn('template <class Protocol_>\n' +
                           'uint32_t {name}(Protocol_* prot_, const ' +
                           obj.name + '* obj)',
                           name=name,
                           output=self._out_tcc)
        else:
            d = scope.defn('template <class Protocol_>\n'
                        'uint32_t {name}(Protocol_* prot_) const',
                        name=method,
                        output=self._out_tcc)

        struct_options = self._get_serialized_fields_options(obj)

        s = d.scope
        if struct_options.has_serialized_fields:
            with s('if ({0}->{1} && '
                    'prot_->protocolType() != {0}->{2})'.format(
                        this,
                        self._serialized_fields_name,
                        self._serialized_fields_protocol_name)):
                out('using apache::thrift::TProtocolException;')
                out('throw TProtocolException(TProtocolException::BAD_VERSION);')
        s('uint32_t xfer = 0;')
        s('xfer += prot_->serializedStructSize("{0}");'.format(obj.name))

        # unions need a case statement to select which member is active
        s0 = s
        if obj.is_union:
            s = s0('switch({0}->getType())'.format(this)).scope

        first = True
        for field in filter(self._should_generate_field, obj.members):
            if self._is_reference(field):
                isset_expr_format = '{0}->{1}'
            elif self._is_optional_wrapped(field):
                isset_expr_format = '{0}->{1}.hasValue()'
            else:
                isset_expr_format = '{0}->__isset.{1}'

            isset_expr = isset_expr_format.format(this, field.name)

            if result == True:
                if first:
                    first = False
                    s1 = s('if ({0})'.format(isset_expr)).scope
                else:
                    s1 = s('else if ({0})'.format(isset_expr)).scope
            elif field.req == e_req.optional:
                s1 = s('if ({0})'.format(isset_expr)).scope
            elif obj.is_union:
                s1 = s.case(obj.name + '::Type::' + field.name).scope
            elif self.flag_terse_writes and not field.req == e_req.required:
                s1 = self._try_terse_write(field, this, s, pointers)
            else:
                s1 = s

            # Add the size of field header + footer
            s1('xfer += prot_->serializedFieldSize("{0}", {1}, {2});'
               ''.format(field.name, self._type_to_enum(field.type),
                                field.key))
            # Add the sizes of field contents
            field_prefix = this + '->'
            field_suffix = ''
            if obj.is_union:
                field_prefix += 'get_'
                field_suffix = '()'
            if pointers and not field.type.is_xception:
                self._generate_serialize_field(
                    s1, field,
                    '(*const_cast<{0}*>('.format(
                            self._type_name(field.type)) + field_prefix,
                    field_suffix + '))',
                    method="serializedSize",
                    struct_method=method,
                    binary_method=method)
            else:
                self._generate_serialize_field(s1, field, field_prefix,
                                               field_suffix,
                                               method="serializedSize",
                                               struct_method=method,
                                               binary_method=method)
            if s1 is not s:
                s1.release()  # if this->__isset.{0}
        if s0 is not s:
            s('case ' + obj.name + '::Type::__EMPTY__:;')
            s.release()
            s = s0

        if struct_options.has_serialized_fields:
            s('xfer += prot_->serializedSizeSerializedData({0}->{1});'
                    .format(this, self._serialized_fields_name))
        s('xfer += prot_->serializedSizeStop();')
        s('return xfer;')
        s.release()

    def _try_terse_write(self, field, this, s, pointers):
        'Generates a terse write predicate for unspecified field, if possible'
        t = self._get_true_type(field.type)
        # Not possible for void/struct/exception.
        if t.is_void or t.is_struct or t.is_xception:
            return s

        # Terse write is unsafe to use without explicitly setting default
        # value as in PHP / Python that would change result of deserialization
        # (comparing with the case when terse_writes is not used): field set
        # in C++ to default value would be deserialized as null / None.
        if self.safe_terse_writes and field.value is None:
            return s

        cmpval = (('(*' + this + '->{0})') if pointers else
                  ('' + this + '->{0}')).format(field.name)

        # For strings, containers - only support predicate if default
        # value is empty.
        if t.is_string:
            if not field.value or len(field.value.string) == 0:
                return s('if (!apache::thrift::StringTraits< {0}>::'
                         'isEmpty({1}))'.format(
                             self._type_name(t), cmpval)).scope
            else:
                return s

        if t.is_container:
            if not field.value:  # only support empty default.
                return s('if (!{0}.empty())'.format(cmpval)).scope
            else:
                return s  # Otherwise, no terse_write possible

        # For base type/enum, check vs. default const value.
        if t.is_base_type or t.is_enum:
            return s('if ({0} != {1})'.format(
                    cmpval, self._member_default_value(field,
                                                       explicit=True))).scope
        return s

    def _generate_struct_writer(self, scope, obj, pointers=False,
                                result=False):
        'Generates the write function.'
        this = 'this'
        if self.flag_compatibility:
            this = 'obj'
            name = '{0}_write'.format(obj.name)
            d = scope.defn('template <class Protocol_>\n' +
                           'uint32_t {name}(Protocol_* prot_, const ' +
                           obj.name + '* obj)',
                           name=name,
                           output=self._out_tcc)
        else:
            d = scope.defn('template <class Protocol_>\nuint32_t {name}'
                           '(Protocol_* prot_) const', name='write',
                           output=self._out_tcc)
        s = d.scope
        name = obj.name
        fields = filter(self._should_generate_field, obj.members)

        struct_options = self._get_serialized_fields_options(obj)

        if struct_options.has_serialized_fields:
            with s('if ({0}->{1} && '
                    'Protocol_::protocolType() != {0}->{2})'.format(
                        this,
                        self._serialized_fields_name,
                        self._serialized_fields_protocol_name)).scope:
                out('using apache::thrift::TProtocolException;')
                out('throw TProtocolException(TProtocolException::BAD_VERSION);')

        s('uint32_t xfer = 0;')
        s('xfer += prot_->writeStructBegin("{0}");'.format(name))

        # unions need a case statement to select which member is active
        s0 = s
        if obj.is_union:
            s = s0('switch({0}->getType())'.format(this)).scope

        first = True
        for field in fields:
            if self._is_reference(field):
                isset_expr_format = '{0}->{1}'
            elif self._is_optional_wrapped(field):
                isset_expr_format = '{0}->{1}.hasValue()'
            else:
                isset_expr_format = '{0}->__isset.{1}'

            isset_expr = isset_expr_format.format(this, field.name)

            if result == True:
                if first:
                    first = False
                    s1 = s('if ({0})'.format(isset_expr)).scope
                else:
                    s1 = s('else if ({0})'.format(isset_expr)).scope
            elif field.req == e_req.optional:
                s1 = s('if ({0})'.format(isset_expr)).scope
            elif obj.is_union:
                s1 = s.case(obj.name + '::Type::' + field.name).scope
            elif self.flag_terse_writes and not field.req == e_req.required:
                s1 = self._try_terse_write(field, this, s, pointers)
            else:
                s1 = s
            # Write field header
            s1('xfer += prot_->writeFieldBegin("{0}", {1}, {2});'.format(
                field.name, self._type_to_enum(field.type), field.key))
            # Write field contents
            field_prefix = this + '->'
            field_suffix = ''
            if obj.is_union:
                field_prefix += 'get_'
                field_suffix = '()'
            if pointers and not field.type.is_xception:
                self._generate_serialize_field(
                    s1, field,
                    '(*const_cast<{0}*>('.format(
                        self._type_name(field.type)) + field_prefix,
                    field_suffix + '))')
            else:
                self._generate_serialize_field(s1, field, field_prefix,
                                               field_suffix)
            # Write field closer
            s1('xfer += prot_->writeFieldEnd();')
            if s1 is not s:
                s1.release()  # if this->_isset.{0}
        if s0 is not s:
            s('case ' + obj.name + '::Type::__EMPTY__:;')
            s.release()
            s = s0
        # Flush any fields stored in serialized form
        if struct_options.has_serialized_fields:
            s('xfer += prot_->writeSerializedData({0}->{1});'.format(
                this, self._serialized_fields_name))
        # Write the struct map
        s('xfer += prot_->writeFieldStop();')
        s('xfer += prot_->writeStructEnd();')
        s('return xfer;')
        s.release()  # the function

    def _generate_serialize_field(self, scope, tfield, prefix='', suffix='',
                                  method='write',
                                  struct_method=None,
                                  binary_method=None):
        'Serializes a field of any type.'
        name = prefix + tfield.name + self._type_access_suffix(tfield.type) + \
                suffix
        pointer = self._is_reference(tfield)
        self._generate_serialize_type(scope, tfield.type, name,
                                      self._is_optional_wrapped(tfield),
                                      method,
                                      struct_method, binary_method, pointer)

    def _generate_serialize_type(self, scope, otype, name,
                                 is_optional_wrapped,
                                 method='write',
                                 struct_method=None,
                                 binary_method=None,
                                 pointer=False):
        'Serializes a variable of any type.'
        ttype = self._get_true_type(otype)
        if struct_method is None:
            struct_method = method
        if binary_method is None:
            binary_method = method

        val_expr = name
        if is_optional_wrapped:
            val_expr += ".value()"

        # Do nothing for void types
        if ttype.is_void:
            raise TypeError('CANNOT GENERATE SERIALIZE CODE FOR void TYPE: '\
                            + name)
        if ttype.is_struct or ttype.is_xception:
            self._generate_serialize_struct(scope, otype, ttype.as_struct,
                                            val_expr,
                                            struct_method, pointer)
        elif ttype.is_container:
            self._generate_serialize_container(scope, ttype.as_container,
                                               val_expr,
                                               method,
                                               struct_method=struct_method,
                                               binary_method=binary_method)
        elif ttype.is_base_type:
            btype = ttype.as_base_type
            base = btype.base
            if base == t_base.void:
                raise CompilerError('Cannot serialize void field in a '
                                    'struct: ' + name)
            elif base == t_base.string:
                if btype.is_binary:
                    txt = '{binary_method}Binary({name});'
                else:
                    txt = '{method}String({val_expr});'
            elif base == t_base.bool:
                txt = '{method}Bool({val_expr});'
            elif base == t_base.byte:
                txt = '{method}Byte({val_expr});'
            elif base == t_base.i16:
                txt = '{method}I16({val_expr});'
            elif base == t_base.i32:
                txt = '{method}I32({val_expr});'
            elif base == t_base.i64:
                txt = '{method}I64({val_expr});'
            elif base == t_base.double:
                txt = '{method}Double({val_expr});'
            elif base == t_base.float:
                txt = '{method}Float({val_expr});'
            else:
                raise CompilerError('No C++ writer for base type ' + \
                        btype.t_base_name(base) + name)
            txt = 'xfer += prot_->' + txt.format(name, **locals())
            scope(txt)
        elif ttype.is_enum:
            scope('xfer += prot_->{0}I32((int32_t){1});'
                  .format(method, val_expr))
        else:
            raise TypeError(("DO NOT KNOW HOW TO SERIALIZE '{0}' "
                             "TYPE {1}").format(name, self._type_name(ttype)))

    def _generate_serialize_struct(self, scope, otype, tstruct, prefix='',
                                   method='write', pointer=False):
        if pointer:
            with scope('if ({0})'.format(prefix)):
                out('xfer += ::apache::thrift::Cpp2Ops< {0}>::{1}('
                    'prot_, {2}.get());'.format(
                        self._type_name(otype),
                        method,
                        prefix))
            with scope('else'):
                out('prot_->writeStructBegin(\"{0}\");'.format(tstruct.name))
                out('prot_->writeStructEnd();')
                out('prot_->writeFieldStop();')
        else:
            scope('xfer += ::apache::thrift::Cpp2Ops< {0}>::{1}('
                  'prot_, &{2});'.format(
                      self._type_name(otype),
                      method,
                      prefix))

    def _generate_serialize_container(self, scope, ttype, prefix='',
                                      method='write', **kwargs):
        tte = self._type_to_enum
        s = scope
        if ttype.is_map:
            s('xfer += prot_->{0}MapBegin({1}, {2}, {3}.size());'.format(
                    method,
                    tte(ttype.as_map.key_type),
                    tte(ttype.as_map.value_type),
                    prefix))
        elif ttype.is_set:
            s('xfer += prot_->{0}SetBegin({1}, {2}.size());'.format(
                    method,
                    tte(ttype.as_set.elem_type),
                    prefix))
        elif ttype.is_list:
            s('xfer += prot_->{0}ListBegin({1}, {2}.size());'.format(
                    method,
                    tte(ttype.as_list.elem_type),
                    prefix))
        ite = self.tmp('_iter')
        typename = self._type_name(ttype)
        with s('for (auto {0} = {1}.begin(); {0} != {1}.end(); ++{0})'.format(
                ite, prefix)):
            if ttype.is_map:
                self._generate_serialize_map_element(out(), ttype.as_map,
                                                     ite, method, **kwargs)
            elif ttype.is_set:
                self._generate_serialize_set_element(out(), ttype.as_set,
                                                     ite, method, **kwargs)
            elif ttype.is_list:
                self._generate_serialize_list_element(out(), ttype.as_list,
                                                      ite, method, **kwargs)
        if ttype.is_map:
            s('xfer += prot_->{0}MapEnd();'.format(method))
        elif ttype.is_set:
            s('xfer += prot_->{0}SetEnd();'.format(method))
        elif ttype.is_list:
            s('xfer += prot_->{0}ListEnd();'.format(method))

    def _generate_serialize_map_element(self, scope, tmap, iter_,
                                        method='write', **kwargs):
        kfield = frontend.t_field(tmap.key_type, iter_ + '->first')
        self._generate_serialize_field(scope, kfield, method=method, **kwargs)
        vfield = frontend.t_field(tmap.value_type, iter_ + '->second')
        self._generate_serialize_field(scope, vfield, method=method, **kwargs)

    def _generate_serialize_set_element(self, scope, tset, iter_,
                                        method='write', **kwargs):
        efield = frontend.t_field(tset.elem_type, "(*{0})".format(iter_))
        self._generate_serialize_field(scope, efield, method=method, **kwargs)

    def _generate_serialize_list_element(self, scope, tlist, iter_,
                                         method='write', **kwargs):
        efield = frontend.t_field(tlist.elem_type, "(*{0})".format(iter_))
        self._generate_serialize_field(scope, efield, method=method, **kwargs)

    # ======================================================================
    # GENERATE STRUCT
    # ======================================================================

    def _generate_cpp2ops(self, compat, obj, scope):
        ns = self._namespace_prefix(self._get_namespace())
        if self.flag_compatibility and compat:
            compat_ns = self._namespace_prefix(
                self._program.get_namespace('cpp'))
        else:
            compat_ns = ns
        compat_full_name = compat_ns + obj.name
        full_name = ns + obj.name

        if not compat and len(obj.members) > 0:
            with scope.defn(
                ('template <> inline '
                 'void Cpp2Ops<{compat_full_name}>::clear('
                 '{full_name}* obj)'.
                 format(**locals())), in_header=True):
                out('return obj->__clear();')

        ops = (('write', True),
               ('read', False),
               ('serializedSize', True),
               ('serializedSizeZC', True))

        with scope.defn('template <> inline constexpr '
                'apache::thrift::protocol::TType '
                'Cpp2Ops<{compat_full_name}>::thriftType()'
                .format(**locals()), in_header=True):
            out('return apache::thrift::protocol::T_STRUCT;')

        for method, is_const in ops:
            const = 'const' if is_const else ''
            with scope.defn(
                ('template <> template <class Protocol> inline '
                 'uint32_t Cpp2Ops<{compat_full_name}>::{method}('
                 'Protocol* proto, {const} {full_name}* obj)'.
                 format(**locals())), name=method, in_header=True):
                if self.flag_compatibility:
                    out(('return {full_name}_{method}(proto, obj);'.
                      format(**locals())))
                else:
                    out('return obj->{method}(proto);'.format(**locals()))

    def _generate_frozen_layout(self, obj, s):
        fields = sorted(obj.as_struct.members, key=lambda field: field.key)
        type_name = self._type_name(obj)

        def visitFields(fmt, fieldFmt, **kwargs):
            return fmt.format(
                type=type_name,
                fields=''.join([
                    fieldFmt.format(
                        type=self._type_name(f.type),
                        name=f.name,
                        _opt='_OPT' if f.req == e_req.optional else
                             '_REQ' if f.req == e_req.required else '',
                        id=f.key,
                        **kwargs) for f in fields]),
                **kwargs)
        s(visitFields(
            'FROZEN_TYPE({type},{fields}{view}{save}{load});',
            '\n  FROZEN_FIELD{_opt}({name}, {id}, {type})',
            view=visitFields(
                '\n  FROZEN_VIEW({fields})',
                '\n    FROZEN_VIEW_FIELD{_opt}({name}, {type})'),
            save=visitFields(
                '\n  FROZEN_SAVE_INLINE({fields})',
                '\n    FROZEN_SAVE_FIELD({name})'),
            load=visitFields(
                '\n  FROZEN_LOAD_INLINE({fields})',
                '\n    FROZEN_LOAD_FIELD({name}, {id})')))

        for (typeFmt, fieldFmt) in [
                ('CTOR', 'CTOR_FIELD{_opt}({name}, {id})'),
                ('LAYOUT', 'LAYOUT_FIELD{_opt}({name})'),
                ('FREEZE', 'FREEZE_FIELD{_opt}({name})'),
                ('THAW', 'THAW_FIELD{_opt}({name})'),
                ('DEBUG', 'DEBUG_FIELD({name})'),
                ('CLEAR', 'CLEAR_FIELD({name})')]:
            # TODO(5484874): Put these back in the .cpp
            s(visitFields('FROZEN_' + typeFmt + '({type},{fields})',
                          '\n  FROZEN_' + fieldFmt))

    def _generate_hash_equal_to(self, obj):
        # Don't generate these declarations if in compatibility mode since
        # they're already declared for cpp.
        if self.flag_compatibility:
            return

        gen_hash = self._has_cpp_annotation(obj, 'declare_hash')
        gen_equal_to = self._has_cpp_annotation(obj, 'declare_equal_to')
        if gen_hash or gen_equal_to:
            full_name = self._namespace_prefix(self._get_namespace()) + obj.name
            with self._types_global.namespace('std').scope:
                if gen_hash:
                    out('template<> struct hash<typename ' + full_name + '> {')
                    out('size_t operator()(const ' + full_name + '&) const;')
                    out("};")
                    if frontend.t_enum == obj.__class__:
                        out('inline size_t '
                            + 'hash<typename ' + full_name + '>::operator()'
                            +    '(const ' + full_name + '& e) const {')
                        out('  return std::hash<int64_t>()(static_cast<int64_t>(e));')
                        out('}')

                if gen_equal_to:
                    out('template<> struct equal_to<typename ' + full_name + '> {')
                    out('bool operator()(const ' + full_name + '&,')
                    out('const ' + full_name + '&) const;')
                    out("};")
                    if frontend.t_enum == obj.__class__:
                        out('inline bool '
                            + 'equal_to<typename ' + full_name + '>::operator()'
                            +    '(const ' + full_name + '& e1,'
                            +    ' const ' + full_name + '& e2) const {')
                        out('  return e1 == e2;')
                        out('}')

    def _generate_cpp_struct(self, obj, is_exception=False):
        # We write all of these to the types scope
        scope = self._types_scope
        self._generate_struct_complete(scope, obj, is_exception,
                                       pointers=False,
                                       read=True,
                                       write=True,
                                       swap=True,
                                       result=False,
                                       to_additional=False,
                                       simplejson=True)

        # We're at types scope now
        scope.release()
        with self._types_global.namespace('apache.thrift').scope:
            self._generate_cpp2ops(False, obj, self._types_scope)

        # std::hash and std::equal_to declarations
        self._generate_hash_equal_to(obj)

        # Re-enter types scope, but we can't actually re-enter a scope,
        # so let's recreate it
        scope = self._types_scope = \
                scope.namespace(self._get_namespace()).scope
        scope.acquire()

    def _generate_object(self, obj):
        self._generate_cpp_struct(obj, obj.is_xception)

    _generate_map = {
        frontend.t_typedef: _generate_typedef,
        frontend.t_enum: _generate_enum,
        frontend.t_struct: _generate_object,
        frontend.t_service: _generate_service,
    }

    def _generate(self, what):
        '''This uses a class-static map of (parse_type -> function that
        generates that kind of object), defined above.'''
        # TODO This feels a little hackish. Maybe change this into individual
        # per-type functions, and also change t_generator.generate_program() to
        # call those functions instead of only _generate().
        try:
            gen_func = self._generate_map[what.__class__]
            gen_func(self, what)
        except KeyError:
            print("Warning: Did not generate {}".format(what))

    def _render_const_value(self, type_, value, explicit=False, literal=False):
        ''' Returns an initializer list rval representing this const
        '''
        t = self._get_true_type(type_)
        if t.is_base_type:
            int32 = lambda x: str(x.integer)
            int64 = lambda x: str(x.integer) + "LL"

            bt = t.as_base_type
            render_string = lambda x: x.string.replace('"', '\\"')
            mapping = {
                t_base.string: lambda x: render_string(x) if literal else
                ('apache::thrift::StringTraits< {0}>::fromStringLiteral(' +
                    '"{1}")').format(self._type_name(t), render_string(x)),
                t_base.bool: lambda x: (x.integer > 0 and 'true' or 'false'),
                t_base.byte: lambda x: ("(int8_t)" + str(x.integer)),
                t_base.i16: lambda x: ("(int16_t)" + str(x.integer)),
                t_base.i32: int32,
                t_base.i64: int64,
                t_base.double:
                    lambda x: (x.type == e_cv_type.integer
                                 and str(x.integer)
                                 or str(x.double)),
                t_base.float:
                    lambda x: (x.type == e_cv_type.integer
                                 and str(x.integer)
                                 or str(x.double))
            }
            if not bt.base in mapping:
                # TODO replace bt.t_base_name(bt.base) with bt.base.LABEL
                # However, (afaik) boost::python apparently doesn't support
                # reverse label lookups for enums
                raise CompilerError("No const of base type " + \
                                    bt.t_base_name(bt.base))
            return mapping[bt.base](value)
        elif t.is_enum:
            return '{0}::{1}'.format(self._type_name(t),
                                    t.find_value(value.integer).name)
        elif t.is_struct or t.is_xception:
            value_map = {}
            for k, v in value.map.items():
                value_map[k.string] = v
            if not value_map:
                return '{0}()'.format(self._type_name(t)) if explicit else None
            fields = filter(self._should_generate_field, t.as_struct.members)
            out_list = ['apache::thrift::FRAGILE']
            for field in fields:
                if field.name in value_map:
                    val = self._render_const_value(field.type,
                                                   value_map[field.name],
                                                   explicit=True)
                else:
                    val = self._member_default_value(field, explicit=True)
                out_list.append(val or "")
            return '{0}({1})'.format(self._type_name(t), ', '.join(out_list))
        elif t.is_map:
            outlist = []
            for key, value in value.map.items():
                key_render = self._render_const_value(t.key_type, key,
                                                      explicit=True)
                value_render = self._render_const_value(t.value_type, value,
                                                        explicit=True)
                outlist.append('{{{0}, {1}}}'.format(key_render, value_render))
            if not outlist:
                return '{}' if explicit else None
            return '{' + ', '.join(outlist) + '}'
        elif t.is_list:
            outlist = []
            for item in value.list:
                field_render = self._render_const_value(
                    t.as_list.elem_type, item, explicit=True)
                outlist.append(field_render)
            if not outlist:
                return '{}' if explicit else None
            return 'std::initializer_list<' + \
                self._type_name(t.as_list.elem_type) + '>{' + \
                ', '.join(outlist) + '}'
        elif t.is_set:
            outlist = []
            for item in value.list:
                field_render = self._render_const_value(t.as_set.elem_type,
                                                        item, explicit=True)
                outlist.append(field_render)
            if not outlist:
                return '{}' if explicit else None
            return 'std::initializer_list<' + \
                self._type_name(t.as_set.elem_type) + '>{' + \
                ', '.join(outlist) + '}'
        else:
            raise TypeError('INVALID TYPE IN print_const_definition: ' + t.name)

    def _generate_layouts(self, objects):
        if not self.flag_frozen2:
            return
        context = self._make_context(self._program.name + '_layouts')
        # TODO(5484874): Remove this hack, which supresses the #include of
        #                layouts.h
        print >> context.impl, '#if 0 // all layouts inlined in layouts.h'
        s = get_global_scope(CppPrimitiveFactory, context)
        if self.flag_compatibility:
            # Delegate to cpp1 layouts
            s('#include "{0}"'.format(self._with_compatibility_include_prefix(
                self._program, self._program.name + '_layouts.h')))
        else:
            s('#include <thrift/lib/cpp2/frozen/Frozen.h>')
            s('#include "{0}"'.format(self._with_include_prefix(self._program,
                self._program.name + '_types.h')))
            # Include other layouts
            for inc in self._program.includes:
                s('#include  "{0}_layouts.h"'
                 .format(self._with_include_prefix(inc, inc.name)))
            with s.namespace('apache.thrift.frozen').scope:
                for obj in objects:
                    self._generate_frozen_layout(obj, out())
        print >> context.impl, '\n#endif'

    def _generate_consts(self, constants):
        name = self._program.name
        # build the const scope
        context = self._make_context(self._program.name + '_constants')
        sg = get_global_scope(CppPrimitiveFactory, context)
        # Include the types header
        sg('#include "{0}"'.format(self._with_include_prefix(self._program,
            self._program.name + '_types.h')))
        sg('#include <thrift/lib/cpp2/protocol/Protocol.h>')
        # Include the thrift1 constants for compatibility mode
        if self.flag_compatibility:
            sg('#include "{0}_constants.h"'
                .format(self._with_compatibility_include_prefix(self._program,
                                                                name)))
        # Open namespace
        sns = sg.namespace(self._get_namespace()).scope

        # COMPATIBILITY MODE
        if self.flag_compatibility:
            cpp1_namespace = self._namespace_prefix(
                self._program.get_namespace('cpp')).lstrip()
            sns('using ' + cpp1_namespace + self._program.name + '_constants;')
            sns('using ' + cpp1_namespace + self._program.name
                + '_constants_codemod;')
            sns.release()
            sg.release()
            return

        # DECLARATION
        s = sns.cls('struct {0}_constants'.format(name)).scope
        with s:
            # Default constructor
            for c in constants:
                inlined = c.type.is_base_type or c.type.is_enum
                value = self._render_const_value(c.type, c.value,
                    literal=inlined)
                if inlined:
                    if c.type.is_string:
                        s('// consider using folly::StringPiece instead of '
                            + 'std::string whenever possible')
                        s('// to referencing this statically allocated string'
                            + ' constant, in order to ')
                        s('// prevent unnecessary allocations')
                    s.defn(('static constexpr {0} const {{name}}_ = {1}{2}'
                        + '{1};').format(
                            'char const *' if c.type.is_string
                                else self._type_name(c.type),
                            '"' if c.type.is_string else '',
                            value if value is not None else "{}"
                        ), name=c.name, in_header=True)
                    sns.impl('constexpr {0} const {1}_constants::{2}_;'
                      .format('char const *' if c.type.is_string else
                        self._type_name(c.type), name, c.name))

                b = s.defn('static {0}{1} const {2}{{name}}()'.format(
                    'constexpr ' if inlined else '', 'char const *' if
                    c.type.is_string else self._type_name(c.type), '' if
                    inlined else '&'), name=c.name, in_header=True).scope
                with b:

                    if inlined:
                        b('return {0}_;'.format(c.name))
                    else:
                        b('static {0} const instance{1};'.format(
                            self._type_name(c.type),
                            '({0})'.format(value) if value is not None else ''
                        ))
                        b('return instance;')

        # CODEMOD TRANSITIONAL
        s = sns.cls(('struct __attribute__((__deprecated__("{1}"))) '
            + '{0}_constants_codemod').format(name, ('{0}_constants_codemod is '
                + 'a transitional class only intended for codemods from the '
                + 'deprecated {0}Constants to {0}_constants. Consider switching'
                + ' to the latter as soon as possible.').format(name))).scope

        with s:
            # Default constructor
            for c in constants:
                value = self._render_const_value(c.type, c.value)
                inlined = (c.type.is_base_type
                    and not c.type.is_string) or c.type.is_enum
                b = s.defn('static {0}{1} const {2}{{name}}()'.format(
                    'constexpr ' if inlined else '', self._type_name(
                    c.type), '' if inlined else '&'), name=c.name,
                    in_header=True).scope
                with b:
                    if inlined:
                        b('return {0};'.format(
                          value if value is not None else '{}'))
                    else:
                        b('static {0} const instance{1};'.format(
                            self._type_name(c.type),
                            '({0})'.format(value) if value is not None else ''))
                        b('return instance;')

        # DEPRECATED
        s = sns.cls('class __attribute__((__deprecated__("{1}"))) {0}Constants'
            .format(name, ("{0}Constants suffers from the 'static "
                + "initialization order fiasco' (https://isocpp.org/wiki/faq/"
                + "ctors#static-init-order) and may CRASH you program. Instead,"
                + " use {0}_constants::CONSTANT_NAME()").format(name))).scope
        with s:
            s.label('public:')
            # Default constructor
            init_dict = OrderedDict()
            for c in constants:
                value = self._render_const_value(c.type, c.value)
                if value:
                    init_dict[c.name] = value
            s.defn('{name}()', name=name + 'Constants', init_dict=init_dict,
                   in_header=True).scope.empty()
            # Define the fields that hold the constants
            for c in constants:
                s()
                s('{0} {1};'.format(self._type_name(c.type), c.name))

        sns.release()  # namespace

        sg.release()   # global scope

    def _generate_fatal(self, program):
        name = self._program.name
        ns = self._get_original_namespace()
        context = self._make_context(name + '_fatal', tcc=False)
        with get_global_scope(CppPrimitiveFactory, context) as sg:
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_types.h')))
            sg()
            sg('#include <thrift/lib/cpp2/fatal/reflection.h>')
            sg()
            sg('#include <fatal/type/list.h>')
            sg('#include <fatal/type/map.h>')
            sg('#include <fatal/type/pair.h>')
            sg('#include <fatal/type/sequence.h>')
            sg()
            if len(ns) > 0:
                with sg.namespace(ns).scope as sns:
                    self._generate_fatal_impl(sns, program)
            else:
                self._generate_fatal_impl(sg, program)

    def _generate_fatal_impl(self, sns, program):
        name = self._program.name
        tag = '{0}_tags::metadata'.format(name)
        safe_ns = self._get_namespace().replace('.', '_')
        str_class = '{0}_{1}__unique_strings_list'.format(safe_ns, name)
        strclsid = 'detail::{0}'.format(str_class)

        order = ['language', 'enum', 'union', 'struct', 'constant', 'service']
        as_map = ['language', 'enum', 'union', 'struct']
        str_from_mapped = ['language']
        items = {}
        items['language'] = self._generate_fatal_lanuage(program)
        items['enum'] = self._generate_fatal_enum(program)
        items['union'] = self._generate_fatal_union(program)
        items['struct'] = self._generate_fatal_struct(program)
        items['constant'] = self._generate_fatal_constant(program)
        items['service'] = self._generate_fatal_service(program)

        with sns.namespace('detail').scope as detail:
            with detail.cls('struct {0}'.format(str_class)).scope as cstr:
                strings = {}
                for i in items:
                    for s in items[i]:
                        if s[0] not in strings:
                            strings[s[0]] = s[1]
                for i in str_from_mapped:
                    for k in items[i]:
                        s = items[i][k]
                        if s[0] not in strings:
                            strings[s[0]] = s[1]
                for i in strings:
                    cstr('using {0} = {1};'
                        .format(i, self._render_fatal_string(strings[i])))
        sns('class {0}_tags {1}'.format(name, '{')).scope
        nname = {}
        for i in order:
            nname[i] = '{0}_{1}__unique_{2}s_list'.format(safe_ns, name, i)

            sns('  struct {0} {1}'.format(nname[i], '{'))
            for n in items[i]:
                sns('    using {0} = {1}::{2};'.format(n[0], strclsid, n[1]))
            sns('};')
        sns('public:')
        for i in order:
            sns('  using {0}s = {1};'.format(i, nname[i]))
        sns('struct metadata {0}'.format('{};'))
        sns('};')
        sns()
        sns('THRIFT_REGISTER_REFLECTION_METADATA(')
        sns('  {0},'.format(tag))
        for item_idx, item in enumerate(order):
            entries = items[item]
            sns('  // {0}s'.format(item))
            if item in as_map:
                sns('  ::fatal::type_map<')
            else:
                sns('  ::fatal::type_list<')
            for idx, i in enumerate(entries):
                cseq = ('{0}::{1}'.format(strclsid, i[0]))
                if item not in as_map:
                    sns('    {0}{1}'.format(
                        cseq, ',' if idx + 1 < len(entries) else ''))
                elif item not in str_from_mapped:
                    sns('    ::fatal::type_pair<{0}, {1}>{2}'.format(
                        i[0], cseq, ',' if idx + 1 < len(entries) else ''))
                else:
                    mseq = ('{0}::{1}'.format(strclsid, entries[i][0]))
                    sns('    ::fatal::type_pair<{0}, {1}>{2}'.format(
                        cseq, mseq, ',' if idx + 1 < len(entries) else ''))
            sns('  >{0}'.format(',' if item_idx + 1 < len(order) else ''))
        sns(');')

    def _get_original_namespace(self):
        if self.flag_compatibility:
            return self._program.get_namespace('cpp')
        return self._get_namespace()

    def _get_scoped_original_namespace(self):
        ns = self._get_original_namespace()
        if len(ns) > 0:
            return '::{0}'.format(ns.replace('.', '::'))
        return ''

    def _render_fatal_string(self, s):
        l = []
        l.extend(s)
        return "::fatal::constant_sequence<char, '{0}'>".format("', '".join(l))

    def _render_fatal_thrift_category(self, ttype):
        while ttype.is_typedef:
            ttype = ttype.type
        if ttype.is_void:
            return 'nothing'
        elif ttype.is_string:
            return 'string'
        elif ttype.is_floating_point:
            return 'floating_point'
        elif ttype.is_base_type:
            return 'integral'
        elif ttype.is_enum:
            return 'enumeration'
        elif ttype.is_list:
            return 'list'
        elif ttype.is_map:
            return 'map'
        elif ttype.is_set:
            return 'set'
        elif ttype.is_struct:
            if ttype.as_struct.is_union:
                return 'variant'
            else:
                return 'structure'
        else:
            return 'unknown'

    def _generate_fatal_lanuage(self, program):
        replacement = {'cpp': '::', 'cpp2': '::', 'php': '_'}
        result = {}
        for i in program.namespaces:
            language = i.key()
            ns = self._program.get_namespace(language)
            safe_ns = ns.replace('.', '__')
            if language in replacement:
                ns = ns.replace('.', replacement[language])
            result[(language, language)] = (safe_ns, ns)
        return result

    def _generate_fatal_enum(self, program):
        name = self._program.name
        ns = self._get_original_namespace()

        context = self._make_context(name + '_fatal_enum', tcc=False)
        with get_global_scope(CppPrimitiveFactory, context) as sg:
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_fatal.h')))
            sg()
            sg('#include <fatal/type/enum.h>')
            sg()
            sg('#include <type_traits>')
            sg()
            if len(ns) > 0:
                with sg.namespace(ns).scope as sns:
                    return self._generate_fatal_enum_impl(sns, program)
            else:
                return self._generate_fatal_enum_impl(sg, program)

    def _generate_fatal_enum_impl(self, sns, program):
        result = []
        for i in program.enums:
            self._generate_fatal_enum_traits(i.name, i.name, i.constants, sns)
            result.append((i.name, i.name))
        return result

    def _generate_fatal_enum_traits(self, name, scoped_name, members, scope):
        scoped_ns = self._get_scoped_original_namespace()
        traits_name = '{0}_enum_traits'.format(scoped_name.replace('::', '_'))
        tag = '{0}_tags::metadata'.format(self._program.name)
        with scope.namespace('detail').scope as detail:
            with detail.cls('struct {0}'.format(traits_name)).scope as t:
                t('using type = {0}::{1};'.format(scoped_ns, scoped_name))
                t('using name = {0};'.format(self._render_fatal_string(name)))
                t()
                with t.cls('struct str'):
                    for i in members:
                        cseq = self._render_fatal_string(i.name)
                        t('using {0} = {1};'.format(i.name, cseq))
                t()
                t('using name_to_value = ::fatal::type_map<')
                for idx, i in enumerate(members):
                    t('  ::fatal::type_pair<')
                    t('    str::{0},'.format(i.name))
                    t('    std::integral_constant<type, type::{0}>'
                        .format(i.name))
                    t('  >{0}'.format(',' if idx + 1 < len(members) else ''))
                t('>;')
                t()
                t('static char const *to_string(type e, char const *fallback)'
                    + ' {0}'.format('{'))
                t('  switch (e) {')
                for i in members:
                    t('    case type::{0}: return "{0}";'.format(i.name))
                t('    default: return fallback;')
                t('  }')
                t('}')
        scope()
        scope('FATAL_REGISTER_ENUM_TRAITS({0}::detail::{1}, {2});'
            .format(scoped_ns, traits_name, tag))
        return traits_name

    def _generate_fatal_union(self, program):
        name = self._program.name
        ns = self._get_original_namespace()

        context = self._make_context(name + '_fatal_union', tcc=False)
        with get_global_scope(CppPrimitiveFactory, context) as sg:
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_fatal.h')))
            sg()
            sg('#include <fatal/type/enum.h>')
            sg('#include <fatal/type/variant_traits.h>')
            sg()
            sg('#include <type_traits>')
            sg()
            if len(ns) > 0:
                with sg.namespace(ns).scope as sns:
                    return self._generate_fatal_union_impl(sns, program)
            else:
                return self._generate_fatal_union_impl(sg, program)

    def _generate_fatal_union_impl(self, sns, program):
        tag = '{0}_tags::metadata'.format(self._program.name)
        scoped_ns = self._get_scoped_original_namespace()
        result = []
        traits = {}
        for i in program.structs:
            if not i.is_union:
                continue
            self._generate_fatal_enum_traits(
                'Type', '{0}::Type'.format(i.name), i.members, sns)
            sns()
            result.append((i.name, i.name))
            with sns.namespace('detail').scope as detail:
                traits[i.name] = self._generate_fatal_union_traits(i, detail)
        if len(traits) > 0:
            sns()
        for i in result:
            sns('FATAL_REGISTER_VARIANT_TRAITS({0}::detail::{1}, {2});'
                .format(scoped_ns, traits[i[1]], tag))
        return result

    def _generate_fatal_union_traits(self, union, scope):
        scoped_ns = self._get_scoped_original_namespace()
        name = '{0}_variant_traits'.format(union.name)
        with scope.cls('class {0}'.format(name)).scope as t:
            with t.cls('struct get'):
                for i in union.members:
                    self._generate_fatal_union_traits_getter(union, i, t)
            t()
            with t.cls('struct set'):
                for i in union.members:
                    self._generate_fatal_union_traits_setter(union, i, t)
            t()
            t('public:')
            t('using type = {0}::{1};'.format(scoped_ns, union.name))
            t('using name = {0};'.format(self._render_fatal_string(union.name)))
            t('using id = type::Type;')
            t()
            with t.cls('struct ids'):
                for i in union.members:
                    t("using {0} = std::integral_constant<id, id::{0}>;"
                        .format(i.name))
            t()
            t('using descriptors = ::fatal::type_list<')
            for idx, i in enumerate(union.members):
                t('  ::fatal::variant_type_descriptor<')
                t('    {0},'.format(self._type_name(i.type)))
                t('    ids::{0},'.format(i.name))
                t('    get::{0},'.format(i.name))
                t('    set::{0}'.format(i.name))
                t('  >{0}'.format(',' if idx + 1 < len(union.members) else ''))
            t('>;')
            t()
            t('static id get_id(type const &variant) {0}'.format('{'))
            t('  return variant.getType();')
            t('}')
            t()
            t('static bool empty(type const &variant) {0}'.format('{'))
            t('  return variant.getType() == id::__EMPTY__;')
            t('}')
        return name

    def _generate_fatal_union_traits_getter(self, union, field, scope):
        ftname = self._type_name(field.type)
        with scope.cls('struct {0}'.format(field.name)):
            scope('{0} const &operator ()({1} const &variant) const {2}'
                .format(ftname, union.name, '{'))
            scope('  return variant.get_{0}();'.format(field.name))
            scope('}')
            scope()
            scope('{0} &operator ()({1} &variant) const {2}'
                .format(ftname, union.name, '{'))
            scope('  return variant.mutable_{0}();'.format(field.name))
            scope('}')
            scope()
            scope('{0} operator ()({1} &&variant) const {2}'
                .format(ftname, union.name, '{'))
            scope('  return std::move(variant).move_{0}();'.format(field.name))
            scope('}')

    def _generate_fatal_union_traits_setter(self, union, field, scope):
        ftname = self._type_name(field.type)
        with scope.cls('struct {0}'.format(field.name)):
            scope('template <typename... Args>')
            scope('void operator ()({1} &variant, Args &&...args) const {2}'
                .format(ftname, union.name, '{'))
            scope('  return variant.set_{0}(std::forward<Args>(args)...);'
                .format(field.name))
            scope('}')

    def _generate_fatal_struct(self, program):
        name = self._program.name
        ns = self._get_original_namespace()
        context = self._make_context(name + '_fatal_struct', tcc=False)
        with get_global_scope(CppPrimitiveFactory, context) as sg:
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_fatal.h')))
            sg()
            sg('#include <fatal/type/traits.h>')
            sg()
            if len(ns) > 0:
                with sg.namespace(ns).scope as sns:
                    return self._generate_fatal_struct_impl(sns, program)
            else:
                return self._generate_fatal_struct_impl(sg, program)

    def _generate_fatal_struct_impl(self, sns, program):
        name = self._program.name
        tag = '{0}_tags::metadata'.format(name)
        safe_ns = self._get_namespace().replace('.', '_')
        strclsprefix = '{0}_{1}__struct_unique_strings_list'.format(
            safe_ns, name)
        dtmclsprefix = '{0}_{1}__struct_unique_data_member_getters_list'.format(
            safe_ns, name)
        mpdclsprefix = '{0}_{1}__struct_unique_member_pod_list'.format(
            safe_ns, name)
        with sns.namespace('detail').scope as detail:
            members = []
            strings = []
            for i in program.structs:
                if i.is_union:
                    continue
                if i.name not in strings:
                    strings.append(i.name)
                for m in i.members:
                    if m.name not in strings:
                        strings.append(m.name)
                    if m.name not in members:
                        members.append(m.name)
            with detail.cls('struct {0}'.format(strclsprefix)).scope as cstr:
                for i in strings:
                    cstr('using {0} = {1};'
                        .format(i, self._render_fatal_string(i)))
            with detail.cls('struct {0}'.format(dtmclsprefix)).scope as dtm:
                for i in members:
                    dtm('FATAL_DATA_MEMBER_GETTER({0}, {0});'.format(i))
            with detail.cls('struct {0}'.format(mpdclsprefix)).scope as mpd:
                for i in members:
                    mpd('template <typename T>')
                    with detail.cls('struct {0}_{1}_struct_member_pod_{2}'
                      .format(safe_ns, name, i)).scope as pod:
                        pod('T {0};'.format(i))
            for i in program.structs:
                if i.is_union:
                    continue
                cnms = detail.cls('struct {0}_{1}'
                    .format(i.name, strclsprefix)).scope
                with cnms:
                    for m in i.members:
                        cnms('using {0} = {1}::{0};'
                            .format(m.name, strclsprefix))
        result = []
        for i in program.structs:
            if i.is_union:
                continue
            result.append((i.name, i.name))
            sns('THRIFT_REGISTER_STRUCT_TRAITS(')
            sns('  {0},'.format(i.name))
            sns('  detail::{0}::{1},'.format(strclsprefix, i.name))
            sns('  {0},'.format(tag))
            sns('  detail::{0}_{1},'.format(i.name, strclsprefix))
            sns('  ::fatal::type_list<')
            for midx, m in enumerate(i.members):
                sns('    ::apache::thrift::reflected_struct_data_member<')
                sns('      detail::{0}::{1},'.format(strclsprefix, m.name))
                sns('      {0},'.format(self._type_name(m.type)))
                sns('      {0},'.format(m.key))
                sns('      detail::{0}::{1},'.format(dtmclsprefix, m.name))
                sns('      ::apache::thrift::thrift_category::{0},'
                    .format(self._render_fatal_thrift_category(m.type)))
                sns('      detail::{0}::{1}_{2}_struct_member_pod_{3}'
                    .format(mpdclsprefix, safe_ns, name, m.name))
                sns('    >{0}'.format(',' if midx + 1 < len(i.members) else ''))
            sns('  >')
            sns(');')
        return result

    def _generate_fatal_constant(self, program):
        name = self._program.name
        ns = self._get_original_namespace()

        context = self._make_context(name + '_fatal_constant', tcc=False)
        with get_global_scope(CppPrimitiveFactory, context) as sg:
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_fatal_enum.h')))
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_fatal_union.h')))
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_fatal_struct.h')))
            sg()
            if len(ns) > 0:
                with sg.namespace(ns).scope as sns:
                    return self._generate_fatal_constant_impl(sns, program)
            else:
                return self._generate_fatal_constant_impl(sg, program)

    def _generate_fatal_constant_impl(self, sns, program):
        result = []
        for i in program.consts:
            result.append((i.name, i.name))
        return result

    def _generate_fatal_service(self, program):
        name = self._program.name
        ns = self._get_original_namespace()
        context = self._make_context(name + '_fatal_service', tcc=False)
        with get_global_scope(CppPrimitiveFactory, context) as sg:
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_fatal.h')))
            sg()
            if len(ns) > 0:
                with sg.namespace(ns).scope as sns:
                    return self._generate_fatal_service_impl(sns, program)
            else:
                return self._generate_fatal_service_impl(sg, program)

    def _generate_fatal_service_impl(self, sns, program):
        name = self._program.name
        safe_ns = self._get_namespace().replace('.', '_')
        strclsprefix = '{0}_{1}__service_unique_strings_list'.format(
            safe_ns, name)
        with sns.namespace('detail').scope as detail:
            cstr = detail.cls('struct {0}_strings'.format(strclsprefix)).scope
            with cstr:
                strings = []
                for s in program.services:
                    for m in s.functions:
                        if m.name not in strings:
                            strings.append(m.name)
                        for a in m.arglist.members:
                            if a.name not in strings:
                                strings.append(a.name)
                for i in strings:
                    cstr('using {0} = {1};'
                        .format(i, self._render_fatal_string(i)))
        result = []
        for i in program.services:
            result.append((i.name, i.name))
        return result

    def _make_context(self, filename,
                      tcc=False,
                      processmap=False,
                      separateclient=False,
                      custom_protocol=False):
        'Convenience method to get the context and outputs for some file pair'
        # open files and instantiate outputs
        output_h = self._write_to(filename + '.h')
        output_impl = self._write_to(filename + '.cpp')
        output_tcc = self._write_to(filename + '.tcc') if tcc else None
        additional_outputs = []
        custom_protocol_h = self._write_to(filename + "_custom_protocol.h") \
                if custom_protocol else None

        if processmap:
            for a, b, c in self.protocols:
                additional_outputs.append(self._write_to(filename + "_processmap_" + a + ".cpp"))
        if separateclient:
            additional_outputs.append(self._write_to(filename + "_client.cpp"))
        header_path = self._with_include_prefix(self._program, filename)

        context = CppOutputContext(output_impl, output_h, output_tcc,
                                   header_path, additional_outputs,
                                   custom_protocol_h)

        print >>context.outputs, self._autogen_comment
        if context.custom_protocol_h is not None:
            print >>context.custom_protocol_h, self._autogen_comment
        return context

    @property
    def out_dir(self):
        return os.path.join(self._program.out_path, self._out_dir_base)

    def in_out_dir(self, filename):
        return os.path.join(self.out_dir, filename)

    # TODO add out_dir(self, program) as well?

    def _include_prefix(self, program, dir_base):
        #assert isinstance(program, frontend.t_program)
        ip = program.include_prefix
        if not self.flag_include_prefix or ip.startswith('/'):
            return ""
        if '/' in ip:
            ip = os.path.dirname(ip)
            return os.path.join(ip, dir_base)
        return ""

    def _compatibility_include_prefix(self, program):
        return self._include_prefix(program, self._compatibility_dir_base)

    def _out_include_prefix(self, program):
        return self._include_prefix(program, self._out_dir_base)

    def _with_compatibility_include_prefix(self, program, *args):
        return os.path.join(self._compatibility_include_prefix(program), *args)

    def _with_include_prefix(self, program, *args):
        return os.path.join(self._out_include_prefix(program), *args)

    def _write_to(self, to):
        return IndentedOutput(open(self.in_out_dir(to), 'w'))

    def init_generator(self):
        name = self._program.name
        # Make output directory
        try:
            os.mkdir(self.out_dir)
        except OSError as exc:
            if exc.errno == errno.EEXIST and os.path.isdir(self.out_dir):
                pass
            else:
                raise

        self._const_scope = None

        # open files and instantiate outputs for types
        context = self._make_context(name + '_types', tcc=True,
                                     custom_protocol=True)
        s = self._types_global = get_global_scope(CppPrimitiveFactory, context)
        self._types_out_impl = types_out_impl = context.impl
        self._types_out_h = types_out_h = context.h
        self._out_tcc = types_out_tcc = context.tcc
        # Enter the scope (prints guard)
        s.acquire()
        # Include base types
        s('#include <thrift/lib/cpp2/Thrift.h>')
        s('#include <thrift/lib/cpp2/protocol/Protocol.h>')
        if not self.flag_bootstrap:
            s('#include <thrift/lib/cpp/TApplicationException.h>')
        if self.flag_optionals:
            s('#include <folly/Optional.h>')
        s('#include <folly/io/IOBuf.h>')
        s('#include <folly/io/Cursor.h>')
        s('#include <boost/operators.hpp>')
        s()
        if self.flag_compatibility:
            # Transform the cpp2 include prefix path into a cpp prefix path.
            s('#include "{0}_types.h"'
              .format(self._with_compatibility_include_prefix(self._program,
                                                              name)))
        # Include other Thrift includes
        for inc in self._program.includes:
            s('#include "{0}_types.h"' \
              .format(self._with_include_prefix(inc, inc.name)))
            print >>context.custom_protocol_h, \
                    '#include "{0}_types_custom_protocol.h"'.format(
                            self._with_include_prefix(inc, inc.name))
        for _, b, _ in self.protocols:
            print >>types_out_tcc, \
                    '#include <thrift/lib/cpp2/protocol/{0}.h>'.format(b)
        s()
        # Include custom headers
        for inc in self._program.cpp_includes:
            if inc.startswith('<'):
                s('#include {0}'.format(inc))
            else:
                s('#include "{0}"'.format(inc))
        s()
        # The swap() code needs <algorithm> for std::swap()
        print >>types_out_impl, '#include <algorithm>\n'

        # using directives
        s()

        # Open namespace
        s = self._types_scope = \
                s.namespace(self._get_namespace()).scope
        s.acquire()

    def close_generator(self):
        # make sure that the main types namespace is closed
        self._types_scope.release()

        if self.flag_implicit_templates:
            # Include the types.tcc file from the types header file
            s = self._types_global
            s()
            s('#include "{0}_types.tcc"'.format(
                self._with_include_prefix(self._program, self._program.name)))
            self._types_global.release()

    def _generate_comment(self, text, style='auto'):
        'Style = block, line or auto'
        lines = text.split('\n')
        if style == 'auto':
            style = len(lines) > 1 and 'block' or 'line'
        if style == 'block':
            return '\n'.join(chain(('/**',), \
                    (' * {0}'.format(line) for line in lines),
                    (' */',)))
        elif style == 'line':
            return '\n'.join('// {0}'.format(line) for line in lines)
        else:
            raise NotImplementedError

    def _is_processed_in_eb(self, function):
        if function.annotations is not None and \
          'thread' in function.annotations.annotations:
            return function.annotations.annotations['thread'] == 'eb'
        return self.flag_process_in_event_base

# register the generator factory
t_generator.GeneratorFactory(CppGenerator)
