19

I want to be able to do the following:

I have an array of strings that contain data types:

string DataTypeValues[20] = {"char", "unsigned char", "short", "int"};

Then later, I would like to create a variable of one of the data types at runtime. I won't know at compile time what the correct data type should be.

So for example, if at runtime I determined a variable x needed to be of type int:

DataTypeValues[3] x = 100;

Obviously this won't work, so how could I do something like this?

Blade3
  • 4,140
  • 13
  • 41
  • 57

14 Answers14

22

The simple answer is that you can't - types need to be known at compile time in C++. You can do something like it using things like boost::any or unions, but it won't be pretty.

  • 4
    Actually, since the set of types is bounded and known at compile time, the appropriate class here would be `boost::variant`. Still not pretty, but a whole lot better. You no longer can stuff a string in. – MSalters May 14 '10 at 14:01
  • 2
    Then how does boost do this? and how does python do this too? As I know, python is built in C/C++. CMIIW. – Hzzkygcs Aug 03 '19 at 04:40
  • 1
    The C++ standard library also has [std::variant](https://en.cppreference.com/w/cpp/utility/variant) for variant types (since C++17). – Anderson Green Apr 23 '21 at 13:43
12

you would have to use unions to achieve something like that, but handling unions is a very difficile matter, so you should choose a container class which wraps the union logic behind an interface like Boost.Variant or Qts QVariant

smerlin
  • 6,446
  • 3
  • 35
  • 58
  • Variant is definitely the way to go, it's almost free too (compared to any which checks the type using RTTI). – Matthieu M. May 14 '10 at 16:46
  • 1
    I agree with Matthieu and smerlin, the Variant types are definitely the way to go. They work extremely well The one thing to be care with is when you extract data from the Variant, you have to ask the object what type is stored in it so that you can store it correctly. – photo_tom May 15 '10 at 00:18
4

You can't. This kind of run-time metaprogramming is not supported in C++.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
3

Everyone saying you can't do this in C++ is missing one obvious solution. This is where you could use a base class, you need to define the commonly used interface there, and then all the derived classes are whatever types you need. Put it in a smart pointer appropriate for a container and there you go. You may have to use dynamic type inference if you can't put enough of the interface in the base class, which is always frowned upon because it's ugly, but it's there for a reason. And dynamically allocating your types probably isn't the most efficient thing, but as always, it depends on what you're using it for.

paul
  • 141
  • 2
  • 2
    The data types the OP mentions are built-in, and not part of any class. It would be necessary to wrap the built-ins into classes, with virtual members, and pass the behavior through. It could be done, but it really, really isn't pretty, and there's got to be a better way to handle the OP's real problem. – David Thornley May 14 '10 at 14:39
2

I think you are really looking for a dynamically-typed language. Embed an interpreter if you must stick with C++!

Or you could implement something akin to the component model using interfaces to work with wrapped data. Start with the cosmic base class - IObject, then implement interfaces for IInteger, IDouble, IString, etc. The objects themselves would then get created by a factory.

Or you could just use void buffers with a factory... That's the age-old way of avoiding static typing in C/C++ (without the use of inheritance-based polymorphism). Then sprinkle in generous amounts of reinterpret_cast.

Jeremy Brown
  • 17,880
  • 4
  • 35
  • 28
2

The closest you can get is with templates:

template<int i> class Data { };
template<> class Data<0> { typedef char type; }
template<> class Data<1> { typedef unsigned char type; }
template<> class Data<2 { typedef short type; }
template<> class Data<3> { typedef int type; }
Data<3>::Type x;

If you need something a lot more complex, Boost has a C++-Python bridge.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 1
    This has to be resolved at compile time, not at runtime. The question specifically says that the type is not known at compile time but rather at runtime, so this would not be a solution. – David Rodríguez - dribeas May 14 '10 at 14:38
2

use union and make your own dynamic class. the pseudocode like:

union all{
    char c;
    unsigned char uc; 
    short s; 
    int i;
}; 

class dynamic{ 
    public:
        char Type; 
        all x; 

        template <class T> 
        dynamic(T y){ 
            int Int;
            char Char;
            unsigned char Uchar;
            short Short;
            if (typeof(y) == typeof(Char)){
                Type = 1;
            }else if (typeof(y) == typeof(Uchar)) {
                Type = 2; 
            }else if (typeof(y) == typeof(Short)) {
                Type = 3;
            }else{
                Type = 4;
            } 

            switch (Type) {
                case 1: x.c = y; break;
                case 2: x.uc = y; break;
                case 3: x.s = y; break ;
                case 4: x.i = y; break ; 
            }
        } 

        auto get() {
            switch(Type) {
                case 1: return x.c; 
                case 2: return x.uc; 
                case 3: return x.s; 
                case 4: retuen x.i; 
            }
        }  

    //also make the operators function you like to use
} ;


however you should avoid using the dynamic type as possible as you can because it is memory inefficient
(in this example, each object of dynamic will takes 5 bytes)

it will also slow down your code (a bit).
if in your example you want to use dynamic type of number variable only to reduce memory usage, you should forget about dynamic and just use the integer as the type (where integer can contain all of char, unsigned char, and short at once).

but if you want to use it because you need a dynamic type between something really different (example between an int and a string or a custom object), then it will be one of your option.

Hzzkygcs
  • 1,532
  • 2
  • 16
  • 24
1

The only thing you can do is manually loop through the types and compare each individual one. There's also the potential to use a factory object here - but that would involve the heap.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 1
    As the types are not related by inheritance, using a factory would be difficult, so say the least. –  May 14 '10 at 12:56
  • There is that. Boost::Variant is probably the easiest solution here. – Puppy May 14 '10 at 16:43
1

Visual Basic's 'Variant' data type is what you are talking about. It can hold anything, primary data types, arrays, objects etc.

"The Collection class in OLE Automation can store items of different data types. Since the data type of these items cannot be known at compile time, the methods to add items to and retrieve items from a collection use variants. If in Visual Basic the For Each construct is used, the iterator variable must be of object type, or a variant." -- from http://en.wikipedia.org/wiki/Variant_type

The above page gives some insights on how variants are used and it shows how OLE is used in C++ for dealing with variants.

Abhijeet Pathak
  • 1,948
  • 3
  • 20
  • 28
1

In your simple example, there would be little benefit in not simply using the widest type in the list as a generic container and casting to the smaller types when necessary (or even relying on implicit casts).

You could get elaborate with unions, classes, polymorphism, RTTI, Boost variants etc, but merely for a list of different width integers it is hardly worth the effort.

It seems to me you have a perceived problem for which you have invented an impractical solution for which you are now asking for help. You'd probably be far better off describing your original problem rather than making your solution the problem!

Clifford
  • 88,407
  • 13
  • 85
  • 165
1

Also, don't forget that all the functions that must operate on this mysterious data type. Most functions are designed to use only one type, such as addition. The functions are overloaded to handle additional types.

How do you know at run-time what the variable type is?

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
1

The only way that come to mind now is the old C style where pointer to void was used like:

void *unkown;

Leter on you can assign any object to it like below:

unkown = (void *)new int(4);

If you know the type in the runtime then you may run specified function on such variable like below:

if(type==0) { // int 
    printf("%d\n", * ((int*)unkown) );
} else {
    // other type
}

This way (casting void*) is used for example when malloc [, etc.] function is used.

I'm not saying it is a good practise when c++ is now much more developed. Still agree with persons that saying it is not the best solution for your problem. But maybe after some redesign you may find it helpful.

You may find also interesting auto type since C++11. http://en.cppreference.com/w/cpp/language/auto

nosbor
  • 2,826
  • 3
  • 39
  • 63
1

I guess this reply would be a few years late. But for people who might happen to view this thread, a possible solution for this would be using variable templates. For example:

template<typename T>
T var;

template<typename T>
T arr[10]; 

int main() {
    int temp;
    var<int> = 2;
    cout << var<int> << ' '; // this would output 2
    var<char> = 'a';
    cout << var<int> << ' '; // var<int> value would be a space character
    cout << var<char> << ' '; // this would output 'a'
    for(int i = 0; i < 10; i++) {
        switch(i % 2) {
            case 0:
                arr<int>[i] = ++temp;
                break;
            case 1:
                arr<char>[i] = 'a' + ++temp;
                break;
    }
    cout << endl;
    for(int i = 0; i < 10; i++) {
        switch(i % 2) {
            case 0:
                cout << arr<int>[i] << ' ';
                break;
            case 1:
                cout << arr<char>[i] << ' ';
                break;
        }
    }
    return 0;
}

The only problem with this, is that you would need to know the variable type of what is currently within the variable(e.g. storing in an integer array what the variable's "id"(the id you would give it), for a specific type). If you do not know or do not have a condition to know what is inside a specific variable or array location, I do not suggest using this.

-1

I try to post it in here, but I had format error. I decided to put a link. Any way you can use (long long) to store addresses because size of address is 8 and size of (long long) also is 8 then it can hold an address.

https://www.flatech.com.au/learning-material/programming/c/object-pointers-to-any-type

Maj
  • 29
  • 4