0

How to check what is a runtime type under a void* pointer. For example, how to write such a function:

void f(void *p) {
  // check if *p is an int or a vector
}

Modern C++ versions (14, 17) are welcome. Future versions are also interesting as an information for the future.

There is no base class nor common virtual methods and simple types are allowed, so How to determine actual object type at runtime in C++; is not exactly relevant.

EDIT:

OK, in certain cases, if the caller knows the real type of the pointer, overloads could be a solution for the simplified example above. But what about something slightly more complex:

using ::std::vector;

void f(vector<void*> v) {
  // check if particular *v[i]'s are ints or vectors
}

Also void* in a function declaration was not my idea.

km9c3uwc
  • 51
  • 5
  • Why would you want to do this?? – Vittorio Romeo May 04 '18 at 10:27
  • 1
    This is something you should not do. This statement might even justify an exclamation mark. Aside from that, you cannot do this. – Jerome Reinländer May 04 '18 at 10:32
  • 1
    There's nothing that can be done at runtime to interrogate what's in a memory referenced by an untyped pointer. Memory is, after all, just a raw sequence of bytes. Every byte is the same as any other byte. C++ does not work this way. – Sam Varshavchik May 04 '18 at 10:35
  • 6
    If you're passing a `void*` as an argument, and you care what type it is; then you're doing it wrong. – UKMonkey May 04 '18 at 10:37
  • The semantics of `void *p` are "`p` may or may not point to some amount of data (or function, sockets, anything) of some sort". That's not useful all that often in C++. – Baum mit Augen May 04 '18 at 11:00
  • if you really need complete type erasure and none of the other solutions work for you, just use std::any http://en.cppreference.com/w/cpp/utility/any – Dan M. May 04 '18 at 12:16

3 Answers3

3

It is not really clear, why you have a void* in the first place. Once you have a void* any information on the actual type is lost and the function you'd like to write is not possible in C++. If you want to call the same method with either a pointer to int or pointer to std::vector you would rather use overloads:

void f(int* p) {
     std::cout << "f called with pointer to int";
}

template <typename T>
void f(std::vector<T>* p) {
     std::cout << "f called with pointer to vector";
}
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Thanks! My initial example was a bit oversimplified. If the caller knows the real type of the pointer then overloads could be a solution for this simple case. I updated the question. – km9c3uwc May 04 '18 at 17:28
  • @km9c3uwc putting the pointers inside a vector does not change the fact that a `void*` carries no information on the actual type of the pointee. Using a `void*` is like saying "I dont want to know what is the type of the object the pointer is pointing to, and because I want to make that explicit I am using a `void*`." There are some rare cases where void pointers are useful, but all I know require you to store the information on the actual type somewhere – 463035818_is_not_an_ai May 04 '18 at 19:10
  • @km9c3uwc there is nothing magic about pointers, they store an adress, thats all. For example, not even a `int*` is guaranteed to point to an `int`, but it is the responsibility of the one using the pointer to make it point to some valid adress. Consider `int* x = 123;`, I am not aware of a portable way to know whether `*x` is a valid `int` – 463035818_is_not_an_ai May 04 '18 at 19:15
  • @km9c3uwc ...that being said, you should find out why the function is supposed to take void pointers as parameters, and if there is no good reason then convince whoever wrote that code to change it – 463035818_is_not_an_ai May 04 '18 at 19:17
0

This is not possible for primitive types, and honestly it is a terrible idea. void* is just an address to anything - there's no information here that can be used to deduce what the actual type of the pointee is.

In practice, one of the reasons why this can be done for polymorphic types is because run-time type information can be stored in the vtable. (Note that the Standard doesn't require the use of a vtable, but it's the most common implementation.)

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • This can't be done for polymorphic types, either. Sure, you can guess that your compiler puts a vtable pointer at a particular offset from the address of the object, but the language definition does not require use of a vtable, nor does it say where the vtable pointer goes if polymorphism is implemented that way. – Pete Becker May 04 '18 at 11:24
  • @PeteBecker I think Vittorio is mentioning the question OP linked, which has a `Base*` instead of a `void*`. That provides a starting point for the language to begin to make sense of what's stored there (i.e. a vtable pointer of a certain type). `void*` carries zero meaning, so nothing can be done. – Quentin May 04 '18 at 11:31
0

Forget about void*. It is a one way ticket; once you step in, there is no way back and all static type info is gone for good. If the set of used types is unbound, go for std::any(c++17 or boost). It may trigger dynamic allocation, but also has small value optimization: http://en.cppreference.com/w/cpp/utility/any But if the set of used types is countable and bound, consider std::variant(c++14 or boost). You may investigate static visitor pattern: http://en.cppreference.com/w/cpp/utility/variant

Red.Wave
  • 2,790
  • 11
  • 17