     cudaStream_t stream)
 {
   // using c++11 std::is_same here
-  const char* type;
-  if (std::is_same<IndexType, unsigned long>::value)
-    type = "unsigned long";
-  else if(std::is_same<IndexType, unsigned int>::value)
-    type = "unsigned int";
-
-  char src[2048];
-  sprintf(src, instanciate_apply3, type, op, A, B, C);
+  const char* index_type = getTypeString<IndexType>();
+  const char* ta_type = getTypeString<Ta>();
+  const char* tb_type = getTypeString<Tb>();
+  const char* tc_type = getTypeString<Tc>();
+
+  char src[4096];
+  sprintf(src, instanciate_apply3, ta_type, tb_type, tc_type, index_type, op, A, B, C);
   const char *headers[] = {apply_header};
   const char *includeNames[] = {"header.h"};
 
   PTXPtr ptx;
-  Apply3Hash hash(op, type, A, B, C);
-  auto found_hash = apply3cache.find(hash);
-  if(found_hash == apply3cache.end())
+  ApplyHash hash(op, std::string(ta_type)+"_"+tb_type+"_"+tc_type, index_type, A, B, C);
+  auto found_hash = applycache.find(hash);
+  if(found_hash == applycache.end())
   {
     ptx = PTXPtr(new PTX());
     compilePTX(src, headers, includeNames, *ptx);
-    apply3cache.emplace(hash, ptx);
+    applycache.emplace(hash, ptx);
   }
   else
     ptx = found_hash->second;

}


/*
// Example op: 'x = y*z'
const char* instanciate_apply3 = "                                      \n\
#define TYPE %s								\n\
#include <header.h>                                                     \n\
struct Op {                                                             \n\
  __device__ __forceinline__						\n\
  void operator()(float* a, float* b, float *c) {			\n\
    float& x = *a;                                                      \n\
    float& y = *b;							\n\
    float& z = *c;							\n\

  launch(ptx->data(), "kernel", args, grid, block, (CUstream)stream);
}


template <typename TensorTypeA>
bool THC_pointwiseApply1(THCState* state,
