def get_header(ops_and_kernels, include_all_ops_and_kernels):
  """Returns a header for use with tensorflow SELECTIVE_REGISTRATION.

  Args:
    ops_and_kernels: a set of (op_name, kernel_class_name) pairs to include.
    include_all_ops_and_kernels: if True, ops_and_kernels is ignored and all op
    kernels are included.

  Returns:
    the string of the header that should be written as ops_to_register.h.
  """
  ops = set([op for op, _ in ops_and_kernels])
  result_list = []

  def append(s):
    result_list.append(s)

  append('#ifndef OPS_TO_REGISTER')
  append('#define OPS_TO_REGISTER')

  if include_all_ops_and_kernels:
    append('#define SHOULD_REGISTER_OP(op) true')
    append('#define SHOULD_REGISTER_OP_KERNEL(clz) true')
    append('#define SHOULD_REGISTER_OP_GRADIENT true')
  else:
    append('constexpr inline bool ShouldRegisterOp(const char op[]) {')
    append('  return false')
    for op in sorted(ops):
      append('     || (strcmp(op, "%s") == 0)' % op)
    append('  ;')
    append('}')
    append('#define SHOULD_REGISTER_OP(op) ShouldRegisterOp(op)')
    append('')

    line = 'const char kNecessaryOpKernelClasses[] = ","\n'
    for _, kernel_class in ops_and_kernels:
      line += '"%s,"\n' % kernel_class
    line += ';'
    append(line)
    append('#define SHOULD_REGISTER_OP_KERNEL(clz) '
           '(strstr(kNecessaryOpKernelClasses, "," clz ",") != nullptr)')
    append('')

    append('#define SHOULD_REGISTER_OP_GRADIENT ' + (
        'true' if 'SymbolicGradient' in ops else 'false'))

  append('#endif')
  return '\n'.join(result_list)