Finding a specific template from just one template parameter. Is it possible?

Viewed 75

Suppose I have something like this:

template <typename T, typename ...args>
static std::map<T, std::tuple<args...>> MyMaps;

Every type is known on compile time. So for every configuration of types added to the map, a new map is created.

Is there a way to search in all instances of map that matches the T parameter with just the key (and the key type)?

EDIT: Sorry for super simplifying my question. I was afraid to make it too big and in the end it missed my intent. But I guess the what I was trying to achieve is impossible in the way I wanted as @Quimby explained.

What I really was trying to do was a debbug helper (actually to Unreal), to track object values something like this for a includable .h (debbughelper.h):

#include <tuple>
#include <vector>
#include <type_traits>

#define WATCHMYOBJECT(object, ...) //TO DO
#define RESCANMYOBJECT(object) //TO DO

template <typename ...T>
void Expand(T...args)
{
    return;
}

template <typename T, typename ...args>
class MyWatcherClass
{
public:
    static void WatchMyObject(T &object, args& ...b);
    static void RescanMyObject(T& MyObject);
    static std::vector<MyWatcherClass*> Instaces;

private:
    std::tuple<args...> MyTuple = std::tuple<args...>();
    std::vector<void*> VoidPointerStorage;
    T* MyObjectPointer;

private:
    MyWatcherClass();
    ~MyWatcherClass();
};

template <typename T, typename ...args>
void MyWatcherClass<T, args...>::WatchMyObject(T &MyObject, args& ...b)
{
    MyWatcherClass<T, args...>* MyClassPointer = new MyWatcherClass;
    InstacedObjects.push_back(MyClassPointer);
    MyObjectPointer = &MyObject;
    int helpint = 0;
    MyClassPointer->MyTuple = std::make_tuple(b...);
    Expand((MyClassPointer->PointerStorage.push_back((void*)&b),1)...);
}

template <typename T, typename ...args>
void MyWatcherClass<T, args...>::RescanMyObject(T &MyObject)
{
    // I have yet to implement, but impossible to call this
    // Compare Instaces[i].MyObjectPointer with &MyObject to find the matching one
    // cast all the void pointers in std::vector<void*> VoidPointerStorage back to typed pointers using the tuple types
    // Get the values derefing the pointers and update on the screen, log, etc
}

And then, with help of some macro magic, some one could do:

// #include <"debbughelper.h">
class MyNormalClass
{
public:
    MyNormalClass(int _MyInt, float _MyFloat, std::string _MyString);
    int MyInt;
    float MyFloat;
    std::string MyString;
};

MyNormalClass::MyNormalClass(int _MyInt, float _MyFloat, std::string _MyString) : MyInt(_MyInt), MyFloat(_MyFloat), MyString(_MyString)
{
}

int main()
{
   MyNormalClass MyObject = MyNormalClass(1, 5.2f, std::string("hello"));
   WATCHMYOBJECT(MyObject, MyObject.MyInt, MyObject.MyFloat, MyObject.MyString);

// do other stuff

   RESCANMYOBJECT(MyObject); //easy, without the need to retype all the members
}

But there is no way to call RescanMyObject without the types of the members.

2 Answers

No.

The underlying problem one would have to solve is determining whether a template variable has been instantiated or not. Otherwise have fun searching through infinitely many possible instantiations.

C++ provides no tools for answering such questions because implementation would be near impossible. Mainly due to separated compilation and linking processes. TL;DR; would be that a translation unit(TU) does not contain enough information yet and the linker is too late as the code has already been generated.

Each translation unit is compiled separately into an object file. Each TU saw (hopefully the same) template definition from which it instantiated all variables used in this TU. But it does not and cannot know about any instantiated variables in other TUs.

Job of the linker is to collect all those object files, resolve the exported and missing symbols, including de-duplication of inline definitions, and finally to create the executable/library.

Only just before the final step, the question could be answered. But at this point, the code has already been generated and cannot be changed. And even if it could involve the compiler to create new code again, what if the new code generates even more instantiations, should the linker try again? That borders on runtime-reflection.

Depends on what you mean by search. You can collect them into a container and then do any kind of processing you like:

#include <any>
#include <iostream>
#include <map>
#include <typeindex>
#include <vector>

std::map<std::type_index,       // key type
         std::vector<std::any>  // pointers to the maps with this key type
         >
    key_to_map;

template <typename T, typename... args>
auto& MyMap() {
  static std::map<T, std::tuple<args...>> map = [] {
    key_to_map[std::type_index(typeid(T))].push_back(&map);
    return decltype(map){};
  }();

  return map;
}

int main() {
  MyMap<int, char, double>();
  MyMap<char, int, double>();
  MyMap<int, short, long>();

  std::cout << "Number of maps with `int` as the key is "
            << key_to_map[std::type_index(typeid(int))].size() << '\n';
}
Related