2010-05-14 5 views
7

Я хочу, чтобы иметь возможность сделать следующее:Объявление типа данных динамически в C++

У меня есть массив строк, которые содержат типы данных:

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

потом, я хотел бы создать переменная одного из типов данных во время выполнения. Во время компиляции я не буду знать, каким должен быть правильный тип данных.

Так, например, если во время выполнения я определил переменную х, необходимые, чтобы быть типа INT:

DataTypeValues[3] x = 100; 

Очевидно, что это не будет работать, так как я мог бы сделать что-то вроде этого?

+4

Я рад, что вы на самом деле не можете сделать это на C++. –

+0

Почему вы не хотите писать 'int x = 100'? Опишите проблему более понятной. –

+7

Поскольку вы не можете сделать это на C++, я предлагаю вам открыть новый вопрос, в котором вы укажете проблему, из-за которой вы захотите сделать это, и спросите, как это можно решить вместо этого на C++. – sbi

ответ

13

Простой ответ: вы не можете - типы должны быть известны во время компиляции на C++. Вы можете сделать что-то подобное, используя вещи, такие как boost :: any или union, но это будет не очень красиво.

+4

Фактически, поскольку набор типов ограничен и известен во время компиляции, соответствующий класс здесь будет «boost :: variant ». Все еще не красиво, но намного лучше. Вы больше не можете вставлять строку. – MSalters

3

Вы не можете. Такой метапрограммирование во время выполнения не поддерживается в C++.

1

Единственное, что вы можете сделать, это вручную перебрать типы и сравнить каждый отдельный. Здесь также можно использовать заводский объект, но это будет связано с кучей.

+1

Поскольку типы не связаны по наследству, использование фабрики было бы трудным, так сказать, наименее. – 2010-05-14 12:56:13

+0

Есть это. Boost :: Вариант, вероятно, является самым простым решением здесь. – Puppy

12

вы должны использовать профсоюзы, чтобы добиться чего-то подобного, но обработка профсоюзы очень несговорчивый дело, так что вы должны выбрать класс контейнера, который оборачивает логику накидной за интерфейс, как Boost.Variant или кварты QVariant

+0

Вариант, безусловно, путь, он почти свободен (по сравнению с любым, который проверяет тип с использованием RTTI). –

+0

Я согласен с Matthieu и smerlin, типы Вариантов - определенно путь. Они работают очень хорошо. Одна вещь, о которой нужно заботиться, - это когда вы извлекаете данные из Variant, вы должны спросить объект, какой тип хранится в нем, чтобы вы могли его правильно хранить. –

2

Каждый говоря, что вы не можете сделать это на C++, отсутствует одно очевидное решение. Здесь вы можете использовать базовый класс, вам нужно определить там обычно используемый интерфейс, а затем все производные классы будут любыми типами, которые вам нужны. Поместите его в умный указатель, подходящий для контейнера, и там вы идете. Возможно, вам придется использовать вывод динамического типа, если вы не можете поместить достаточное количество интерфейса в базовый класс, который всегда нахмурился, потому что он уродлив, но он существует по какой-то причине. И динамическое распределение ваших типов, вероятно, не самая эффективная вещь, но, как всегда, это зависит от того, для чего вы ее используете.

+1

Типы данных, которые упоминаются в OP, встроены, а не являются частью любого класса. Было бы необходимо обернуть встроенные в классы, с виртуальными членами и пройти через поведение. Это можно сделать, но это действительно так, на самом деле не очень красиво, и должен быть лучший способ справиться с реальной проблемой OP. –

1

Я думаю, что вы действительно ищете динамически типизированный язык. Вставьте переводчика, если вы должны палкой с C++!

Или вы можете реализовать что-то похожее на модель компонента, используя интерфейсы для работы с обернутыми данными. Начните с космического базового класса - IObject, затем реализуйте интерфейсы для IInteger, IDouble, IString и т. Д. Затем сами объекты будут созданы на заводе.

Или вы можете просто использовать пустотные буферы с фабрикой ... Это вековой способ избежать статической типизации на C/C++ (без использования полиморфизма на основе наследования). Затем посыпать щедрым количеством reinterpret_cast.

1

Тип данных «Вариант» Visual Basic - это то, о чем вы говорите. Он может содержать все, первичные типы данных, массивы, объекты и т. Д.

«Класс Collection в OLE Automation может хранить элементы разных типов данных. Поскольку тип данных этих элементов не может быть известен во время компиляции, методы добавлять элементы и извлекать элементы из вариантов использования коллекции. Если в Visual Basic используется для каждой конструкции, переменная итератора должна быть типа объекта или варианта.. "- от http://en.wikipedia.org/wiki/Variant_type

выше страница дает некоторое представление о том, как используются варианты, и это показывает, как OLE используется в C++ для работы с вариантами

1

Ближайший вы можете получить с помощью шаблонов:

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; 

Если вам нужно что-то гораздо более сложный, подталкивания имеет C++ -. Python мост

+1

Это должно быть разрешено во время компиляции, а не во время выполнения. Вопрос конкретно говорит о том, что тип не известен во время компиляции, а скорее во время выполнения, поэтому это не будет решением. –

1

в вашем простом примере, было бы мало пользы в не просто используя самый широкий тип в списке в качестве общего контейнера и литье в меньшие типы, когда это необходимо (или даже полагаться на неявные слепки).

Вы можете получить подробные сведения о союзах, классах, полиморфизме, RTTI, вариантах Boost и т. Д., Но просто для списка различных целых чисел ширины вряд ли стоит усилий.

Мне кажется, у вас есть воспринимаемая проблема, для которой вы изобрели непрактичное решение, за которое вы сейчас просите о помощи. Вероятно, вам будет намного лучше описать вашу оригинальную проблему, а не решить проблему.

1

Кроме того, не забывайте, что все функции, которые должны работать с этим таинственным типом данных. Большинство функций предназначены для использования только одного типа, например добавления. Функции перегружены для обработки дополнительных типов.

Как вы знаете, во время выполнения, какой тип переменной?

0

Единственный способ, который приходит на ум теперь старый стиль C, где указатель аннулированию был использован как:

void *unkown; 

Leter на вы можете назначить любой объект к нему, как показано ниже:

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

Если вы знаете тип во время выполнения, то вы можете запустить указанную функцию на такой переменной, как показано ниже:

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

Этот способ (casting void *) используется, например, когда используется функция malloc [и т. Д.].

Я не говорю, что это хорошая практика, когда C++ теперь намного более развит. Все еще согласны с лицами, говорящими, что это не лучшее решение вашей проблемы. Но, возможно, после некоторой редизайна вы можете найти это полезным.

Вы также можете найти интересный тип авто с C++ 11. http://en.cppreference.com/w/cpp/language/auto

0

Я думаю, что этот ответ будет несколько лет позже. Но для людей, которые могли бы просмотреть этот поток, возможным решением для этого будет использование переменных шаблонов. Например:

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; 
} 

Единственная проблема с этим, что вы должны знать тип переменной, что в настоящее время в переменной (например, хранение в целочисленном массиве, что в переменной «идентификатор» (идентификатор вам дал бы это), для определенного типа). Если вы не знаете или не знаете, что находится внутри определенной переменной или местоположения массива, я не предлагаю использовать это.