0

Can typeid (or some other way to dynamically pass the type) be used to invoke a templated function.

Ultimately I need a conversion function which will convert data buffers from about a dozen source types to a dozen destination types which leads to hundred cases to statically code. It would be nice to be able to pass type information for source and destination which would automatically build the appropriate template function and invoke it.

Here is the simplified code that demonstrates what I am trying to do:

template<typename T>
void myFunc(T buf, int val) { *buf = val; }

const std::type_info& GetTypeInfo(int csType)
{
  switch(csType)
  {
    case dsCHAR:
    {
      return typeid(char*);
    }
    case dsWCHAR:
    {
      return typeid(wchar_t*);
    }
    default:
    {
      return typeid(int*);
    }
  }
}

void convert(void* buf, int csType, char* src, int len)
{
  const std::type_info& theType = GetTypeInfo(csType);
  for(int ix = 1; ix < len; ix++)
  {
    myFunc<theType>(&dynamic_cast<theType>(buf)[ix], src[ix]); // <- This fails to compile
  }
}

Using type_info& with the template or in a cast is not allowed by the compiler and I have not been able to figure out how to get around it, if it is even possible.

Tomasz
  • 343
  • 2
  • 9
  • 2
    Types must be known at compile time. `typeid` is RTTI (**Runtime** type information). – Fantastic Mr Fox Feb 10 '23 at 03:47
  • Obviously I know that. I am looking for any way to dynamically choose the version of a templated function to use without creating a static combination of all possible template invocations. – Tomasz Feb 10 '23 at 03:56
  • Maybe it helps: https://stackoverflow.com/a/73128153/8649091 – Nimrod Feb 10 '23 at 03:58
  • The question at the top was: `Can typeid (or some other way to dynamically pass the type) be used to invoke a templated function.` Which indicated to me that you didn't know that. The answer is no, you will need to provide all the types (or generate type list with some TMP) in order to do this. You cannot use `typeid` this way. – Fantastic Mr Fox Feb 10 '23 at 03:59
  • @Nimrod it is helpful, but still I will need 84 cases in the ultimate solution of my problem, as there is 7 source types and 12 destination types for my conversion function. But I guess all the combinations would have to be enumerated anyway for the compiler to generate the template function instances for each possible combination. – Tomasz Feb 10 '23 at 05:41
  • If T is a non class type use static_cast, if T is a class type you should not even use dynamic_cast, and you should use memcpy or std::bitcast to copy memory to an instance of the target type – Pepijn Kramer Feb 10 '23 at 06:56
  • It sounds like you could benefit from a reflection library that provides more functionality than plain old C++ RTTI. You could check out libraries like RTTF. Basically, you need to register all types and functions that you want to use dynamically (yes, all 84 versions) once in a static type/function registry. – joergbrech Feb 10 '23 at 07:29
  • 1
    Can you explain WHAT you are trying to do? Maybe this is just not the best HOW. If it is binary serialization that is a hard problem an things like protobuf or a serialization library are indeed better options. (.e.g https://protobuf.dev/getting-started/cpptutorial/) – Pepijn Kramer Feb 10 '23 at 07:37
  • This is for a communication subsystem that is user configurable, such that the user can define the data type in the message and the target data type in a data store in an .xml configuration file and I take care of the identification of the message and translation of the data from every possible source type that arrives in a message to every possible destination type that can be defined in the Data Store, hence I need a complete set of data translations. – Tomasz Feb 10 '23 at 09:42
  • I was looking to eliminate repetitive code as in the future I will need to extend with change detection and other features, so updating 84 similar blocks of code would be tedious and error prone. – Tomasz Feb 10 '23 at 09:42
  • @Pepijn Kramer Mostly basic types and arrays thereof . I use dynamic_cast only to indicate that I was looking for dynamic selection of the template instance to use at runtime. – Tomasz Feb 10 '23 at 09:47
  • I would not write such a communication system myself. Layout of memory is pretty machine (and compiler setting!) dependent. So in general I advice you not to rely on memory layout at all. For communication use libraries, this is exactly what proto/grpc will do for you. And best of all it is used in a lot of places (industry standard) so learning about it is very useful (in many jobs). And you can find tons of resources and online help (https://grpc.io/docs/languages/cpp/quickstart/) – Pepijn Kramer Feb 10 '23 at 09:53
  • Generally I aggrege with you, but we need the performance (need to process about 1200 messages every 100ms & a bigger dump of about 60000 messages once per second). With multithreading and 8 cores we hit >70% CPU. Right now I have it unrolled to all the cases, but that is a maintenance nightmare where as most are just cast operations, so lend them selves to template really well. Target machine will always be Windows 10 on Intel, so there is no issue with memory layout changing. We tried using commercial lib but that would not cut it, so I am writing my own. – Tomasz Feb 13 '23 at 03:02

1 Answers1

2

At the first you are using dynamic_cast ... and it's in the runtime, and the next instruction is a function that is template<typename T> ... that is in compile time!

so it's not possible , you should decide do you want to do that in runtime or compile-time.

I think you need something like this(GodLambda :D)


enum CHARS { dsCHAR, dsWCHAR };
using csType = CHARS;
using MyFunc = std::function<bool(void*)>;
std::map<csType, MyFunc> godLambda = {
    {dsCHAR,
     [](void* ptr) -> bool {
       auto theType = reinterpret_cast<char*>(ptr);
       if (nullptr == theType) return false;
       // my func specialize:
       return true;
     }},
    {dsWCHAR,
     [](void* ptr) -> bool {
       auto theType = reinterpret_cast<wchar_t*>(ptr);
       if (nullptr == theType) return false;
       // my func specialize:
       return true;
     }}
};
Another HM
  • 148
  • 10