class_docstr: Class for an `Op` with an external C implementation.
  
  One can inherit from this class, provide its constructor with a path to
  an external C source file and the name of a function within it, and define
  an `Op` for said function.
docstr: Construct a pair of C ``#define`` and ``#undef`` code strings.
docstr_tok: ["Construct","a","pair","of","C","``","#","define","``","and","``","#","undef","``","code","strings","."]
code: def get_c_macros(
      self, node: Apply, name: str, check_input: Optional[bool] = None
  ) -> tuple[str, str]:
      "Construct a pair of C ``#define`` and ``#undef`` code strings."
      define_template = "#define %s %s"
      undef_template = "#undef %s"
      define_macros = []
      undef_macros = []
  
      if check_input is None:
          check_input = getattr(self, "check_input", True)
  
      if check_input:
          # Extract the various properties of the input and output variables
          variables = node.inputs + node.outputs
          variable_names = [
              f"INPUT_{i}" for i in range(len(node.inputs))
          ] + [f"OUTPUT_{i}" for i in range(len(node.outputs))]
  
          # Generate dtype macros
          for i, v in enumerate(variables):
              if not isinstance(v.type, HasDataType):
                  continue
  
              vname = variable_names[i]
  
              macro_items = (f"DTYPE_{vname}", f"npy_{v.type.dtype}")
              define_macros.append(define_template % macro_items)
              undef_macros.append(undef_template % macro_items[0])
  
              d = np.dtype(v.type.dtype)
  
              macro_items_2 = (f"TYPENUM_{vname}", d.num)
              define_macros.append(define_template % macro_items_2)
              undef_macros.append(undef_template % macro_items_2[0])
  
              macro_items_3 = (f"ITEMSIZE_{vname}", d.itemsize)
              define_macros.append(define_template % macro_items_3)
              undef_macros.append(undef_template % macro_items_3[0])
  
      # Generate a macro to mark code as being apply-specific
      define_macros.append(
          define_template % ("APPLY_SPECIFIC(str)", f"str##_{name}")
      )
      undef_macros.append(undef_template % "APPLY_SPECIFIC")
  
      for n, v in self.__get_op_params():
          define_macros.append(define_template % (n, v))
          undef_macros.append(undef_template % (n,))
  
      return "\n".join(define_macros), "\n".join(undef_macros)
  
imports: import inspect
  import os
  import re
  import warnings
  from collections.abc import Collection
  from re import Pattern
  from typing import TYPE_CHECKING, Any, Callable, ClassVar, Optional, Union, cast
  import numpy
  from pytensor.configdefaults import config
  from pytensor.graph.basic import Apply, Variable
  from pytensor.graph.op import ComputeMapType, Op, StorageMapType, ThunkType
  from pytensor.graph.type import HasDataType
  from pytensor.graph.utils import MethodNotDefined
  from pytensor.link.c.interface import CLinkerOp
  from pytensor.link.c.params_type import ParamsType
  from pytensor.utils import hash_from_code
label_desc: Use image node_id 13 for calling the ExternalCOp obj's underlying member method code with example usage: obj.get_c_macros(node, name, check_input) and returns: str,  str