Как реализовать стек, используя массив, который поддерживает элементы разного типа.
например Он должен работать с символами, целыми числами, числами с плавающей запятой и двойными числами.
Я реализовал это с помощью void
указатели. Ниже C
реализация:
void push( void** stack, int* top, void* data, size_t size )
{
unsigned i;
++*top;
stack[*top] = malloc( size );
for( i = 0; i < size; ++i )
( (char*)stack[*top] )[i] = ( (char*)data )[i];
}
int main()
{
void* stack[10];
int top = -1, data = 10;
char ch = 'a';
push( stack, &top, (void*)&data, sizeof( int ) );
push( stack, &top, (void*)&ch, sizeof( char ) );
printf( "%d ", *(int*)stack[0] );
printf( "%c ", *(char*)stack[1] );
return 0;
}
Код работает отлично для меня.
Проблема с вышеприведенной реализацией заключается в том, что тип данных должен быть известен заранее.
Существует ли способ реализовать его, не зная предварительной информации о типе данных, с которыми нужно работать? [Я знаю, что это невозможно в C, можем ли мы сделать это в C ++, если да, то как? ]?
Если я понимаю ваше требование, вы можете использовать boost::any
чтобы достичь этого в C ++.
#include <boost/any.hpp>
#include <vector>
class AnyStack {
std::vector<boost::any> vec;
public:
template <class T> void push (const T &e) {
boost::any v = e;
vec.push_back(v);
}
class Proxy {
friend class AnyStack;
std::vector<boost::any> &vec;
Proxy(std::vector<boost::any> &v) : vec(v) {}
public:
template <typename T> operator T () {
boost::any v = vec.back();
vec.pop_back();
return boost::any_cast<T>(v);
}
};
Proxy pop () { return Proxy(vec); }
boost::any top () { return vec.back(); }
};
Как предлагали другие, вы можете использовать RTTI для определения типа элемента в стеке. В приведенном ниже примере демонстрируется процедура вывода, которая делает это.
#include <iostream>
#include <string>
std::ostream & operator << (std::ostream &os, const boost::any &a) {
if (a.type() == typeid(char)) {
return os << boost::any_cast<char>(a);
}
if (a.type() == typeid(int)) {
return os << boost::any_cast<int>(a);
}
if (a.type() == typeid(float)) {
return os << boost::any_cast<float>(a);
}
if (a.type() == typeid(double)) {
return os << boost::any_cast<double>(a);
}
if (a.type() == typeid(std::string)) {
return os << boost::any_cast<std::string>(a);
}
}
int main () {
AnyStack a;
a.push(3);
std::cout << a.top() << std::endl;
a.push(std::string("hello"));
std::cout << a.top() << std::endl;
return 0;
}
Это может быть сделано в C. Сначала вам нужно перечисление, описывающее содержащийся тип
typedef enum {TYPE_INT, TYPE_CHAR, TYPE_STRING, ... } contained_type_t;
Тогда поместите все в структуру
typedef struct {
contained_type_t contained_type;
union {
int int_value;
char char_value;
void* pointer_value;
... and so on ...
} data;
};
Вы могли бы взглянуть на GObject модель и ее использование в GLib, реализация общий списки, которые можно смешивать типы Интересно.