2

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.

  • Short answer: Not really. – NathanOliver Oct 04 '21 at 18:47
  • You could make a `std::map`-like container which during construction registers each instance to a global list. You'd need some sort of type-punning and careful application of the rule of 3/5/0. Then, you could have a list of all generated instances, though using that list will be tricky. You'll probably need to also abstract `std::tuple` to do anything useful with it. – François Andrieux Oct 04 '21 at 18:50
  • 4
    Do you mean template instances (classes) or their instances (objects)? What do you mean by _searching_ them? Searching them when? What should be a result of such searching? Exemplary code and result would be useful. – Daniel Langr Oct 04 '21 at 18:52
  • Are you looking for something like this? https://stackoverflow.com/questions/24702235/c-stdmap-holding-any-type-of-value – unddoch Oct 04 '21 at 18:54
  • 1
    If you have a way to list all variables/types, then it is just searching from this list... Issue is that this "list" content might be spread across TU in general cases, so cannot be generated automatically. – Jarod42 Oct 04 '21 at 18:56
  • 1
    I get a feeling of XY question, mixed with ["There is no useful universal class: a truly universal carries no semantics of its own. "](https://www.stroustrup.com/bs_faq2.html#object) needed for the result of the search. What exactly are you trying to solve? – Quimby Oct 04 '21 at 19:15

2 Answers2

3

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.

Quimby
  • 17,735
  • 4
  • 35
  • 55
1

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';
}
Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • Nice idea with the registration, although how would one use the resulting vector? I do not see an easy way how to convert `std::any` elements back to maps. – Quimby Oct 04 '21 at 19:11
  • 1
    @Quimby that is going to require some more knowledge about the problem being solved. For instance, it is possible that OP is doing their own bookkeeping for the types being used, etc. I am not saying `std::any` is the best solution, this a simple example. – Aykhan Hagverdili Oct 04 '21 at 19:16