Array of multiple types

Hello guys,

ive written this code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class boundList
{

    struct node{ void *elem{nullptr}; node *next{nullptr}; };

    node anchor;
    node *_Last;
public:
    template<typename node1_t>
    boundList(node1_t *f_elem)
    {
        anchor.elem = f_elem;
        _Last = &anchor;
    }
    template<typename _Item>
    void append(_Item* n_elem)
    {
        _Last->next = new node{n_elem};
        _Last = _Last->next;
    }
    template<typename _OutType>
    _OutType get(const int index)
    {
        int i = 0;

        node *tmp = &anchor;

        while (i < index)
        {
            tmp = tmp->next;
            i++;
        }
        return *static_cast<_OutType*>(tmp->elem);
    }
     ~boundList()
     {
         node *tmp = anchor.next;
         node *next;
         while (tmp != _Last)
         {
             next = tmp->next;
             delete tmp;
             tmp = next;
         }
     }
};


to create a class that takes any types and puts them together by storing the next pointer and so on. I then realized i have to convert them to void * to be able to do so. Now, if any object is requestes by the user, he has to provide the index AND its type to convert back from void*.
how can I get it to only have to pass the index and have boudList figure out the type itself and return the correct type?
Ive seen that std::tuble is capable of doing so, so there must be a possibility?!

Thanks in advance


Luke
Last edited on
Here is a memory address. I call its type "void". Please tell me the actual type of the object in that location. I bet you can't.


std::tuple is a template and the exact type of instatiation is set when you write the code (aka "compile-time").

Even std::variant knows its type(s) on compile-time: https://en.cppreference.com/w/cpp/utility/variant

Run-time polymorphism does use class inheritance, but even there the allowed types are related.
You might like to have a look at std::any

https://en.cppreference.com/w/cpp/utility/any

You could then have a list with each element in the node of type std::any
Ok, thank you keskiverto.
I never asked for types not to be kown in runime.
Instead i asked myself how std::tuple sets the exact type of instatiation at comp-time and wanted to realize that as well
tuple, like vector and others, are simply using c++ templates at compile time.

templates, under the hood, actually create the type at compile time.
that is, if you say vector<my_weird_thing> to get a vector of some class you made, at compile time the compiler effectively crafts something like this:
class vector
{
my_weird_thing * ptr; //a place holder here is swapped for the real thing
//more stuff
};

and compiles the new class as if it were created for your type all along.
This is a massive oversimplification, but if you want to know more, you will need to look at how an actual c++ compiler handles templates or a paper about that topic. I honestly don't know much more than this simple description of the process, but I am sure it is recursive (you can have templates of templates of templates...) and likely a bit complicated down in the details.
Thank you very much jonnin.
I will have a closer look at that.
Is there no option to store a type information in sth like a variable that i can later cast void * to get the type back?
1
2
3
4
5
6
template<typename T>
auto xy(T&&)
{
typesaver savethistype = T;
return *static_cast<savethistype *>(void_ptr);
}

of course you can store its type. The question is, can you store the type of something you have never seen before and get it back?

read this. Its a mix of "no can do" and some clever code that "almost gets you there" which may or may not work for your needs.
https://stackoverflow.com/questions/2562176/storing-a-type-in-c

However, what you are doing is really sort of fighting the design of C++. C++ is a very strongly typed language, and you are trying to weaken/break that. Note that void*s are powerful but crude, low level C tools that as you have seen do not play nice with some advanced c++. They are awesome for things like thread functions, where the parameters are sent to the generic tool as a void pointer and passed along back to your actual thread function, where... YOU KNOW THE TYPE .. and you simply cast it back to what it was. Nothing in between tried to USE it, its just passed along and ignored until it gets back somewhere that you know what it was. It was never meant to be used to lose the type and then recover it back without any help.
Last edited on
We can query the type of the contained entity with std::any::type
https://en.cppreference.com/w/cpp/utility/any/type
It yields const std::type_info&.

With it we could do things like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <any>
#include <string>
#include <vector>
#include <complex>

template < typename T > bool is( const std::any& a ) noexcept { return a.type() == typeid(T) ; }

std::ostream& operator<< ( std::ostream& stm, const std::any& a )
{
    if( a.has_value() == false ) return stm << "nothing at all" ;
    else if( is<int>(a) ) return stm << "int: " << std::any_cast<int>(a) ;
    else if( is<double>(a) ) return stm << "double: " << std::any_cast<double>(a) ;
    else if( is<std::string>(a) ) return stm << "std::string: '" << std::any_cast<const std::string&>(a) << '\'' ;
    else if( is<const char*>(a) ) return stm << "C-style NTBS (const char*): '" << std::any_cast<const char*>(a) << '\'' ;
    else return stm << "something else" ;
}

int main()
{
    std::vector<std::any> cabbages_and_kings { 123, 45.67, std::string("hello"), "world", std::any{}, std::complex<double>{} } ;
    cabbages_and_kings.push_back(890123) ;
    for( const std::any& a : cabbages_and_kings ) std::cout << a << '\n' ;
}

http://coliru.stacked-crooked.com/a/58acd983fd0d7fb3

For something more elaborate, see:
The example demonstrates std::any visitor idiom with ability to register new visitors at compile- and run-time.
https://en.cppreference.com/w/cpp/utility/any/type#Example


If you know types during compile-time, then you know types and can essentially do:
1
2
3
4
5
std::string text;
void* ptr = &text

// later
static_cast<std::string*>(ptr)

You do know the true type of the object that ptr points to on line 5, because you wrote lines 1 and 2.

However, if the type is determined during runtime, for example by user input, then you do need something extra.

The std::any does use typeid, even on runtime. See https://en.cppreference.com/w/cpp/utility/any/type
Topic archived. No new replies allowed.