c ++ создает непрерывный массив типа, который не известен во время компиляции - PullRequest
0 голосов
/ 06 января 2010

скажем, у меня есть несколько разных определений структуры (на самом деле у меня есть около 50 таких определений):

struct Type1{
    int i;
    float f;
};

struct Type2{
    bool b1;
    bool b2;
    double d;
};

они все POD, но могут содержать совершенно разные данные.

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

Какя сделал бы это?

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

vector<myTypeHashMap[flagsInt]> myVect;

?

Я знаю, что это идет довольно мета-программирование(из которых я понятия не имею :)), но, может быть, есть способ сделать это?

спасибо

спасибо

Ответы [ 12 ]

1 голос
/ 06 января 2010

Вы можете использовать какую-то фабрику. Найдите в Google "фабричный шаблон с ++".

Простой пример кода, объясняющего это:

enum TypeCode { FOO, BAR, ... };

void* makeInstance( TypeCode t ) {
  switch( t ) {
  case FOO: return new FooStruct;
  case BAR: return new BarStruct;
  ...
  }
}

void* makeArray( TypeCode t, size_t len ) {
  switch( t ) {
  case FOO: return new FooStruct[len];
  case BAR: return new BarStruct[len];
  ...
  }
}

РЕДАКТИРОВАТЬ Пример сопоставления в стиле ООП TypeCode с некоторыми функциями и описанием типа:

// base class .. you may add common functionality
class TypeTraitBase {
public:
  virtual void* newStruct() const = 0;
  virtual void* newArray( size_t len ) const = 0;
  virtual size_t getSize() const = 0;
  virtual TypeCode getCode() const = 0;
  // add whatever else you need.. e.g.
  virtual bool isNumeric() const { return false; }
};

template <TypeCode TCode, typename TStruct>
class TypeTrait : public TypeTraitBase {
public:
  virtual TStruct* newStruct() const { return new TStruct; }
  virtual TStruct* newArray( size_t len ) const { return new TStruct[len]; }
  virtual size_t getSize() const { return sizeof(TStruct); }
  virtual TypeCode getCode() const { return TCode; }
};

/* OPTIONAL...
// you may add specializations for some types
// - though it is required only if you want something like isNumeric(),
// - newStruct, newArray and so on do not require specializations!
template < INTEGER, IntegerStruct >
class TypeTrait : public TypeTraitBase {
public:
  virtual TStruct* newStruct() const { return new IntegerStruct; }
  virtual TStruct* newArray( size_t len ) const { return new IntegerStruct[len]; }
  virtual size_t getSize() const { return sizeof(IntegerStruct); }
  virtual TypeCode getCode() const { return INTEGER; }
  virtual bool isNumeric() const { return true; }
};
*/

class TypeTraitMgr {
  static std::map<TypeCode,TypeTraitBase*> traits;
public:
  static void reg( TypeTraitBase* tt ) { traits[tt->getCode()] = tt; }
  static void cleanup() { /* delete all TypeTraits in traits */ }
  static TypeTraitBase* get( TypeCode code ) { return traits[code]; }
};

// in .cpp file: instantiate the static member:
std::map<TypeCode,TypeTraitBase*> traits;


// somewhere before you use it, register all known types:
TypeTraitMgr::reg( new TypeTrait<FOO,YourFOOStruct> );
TypeTraitMgr::reg( new TypeTrait<BAR,YourBARStruct> );

// use it...
void* foo = TypeTraitMgr::get( FOO )->newStruct();
size_t size_of_foo = TypeTraitMgr::get( FOO )->getSize();

// on shutdown, cleanup type traits (they were allocated on heap, delete required)
TypeTraitMgr::cleanup();

Этот код не был проверен, он может содержать ошибки;)

Обратите внимание, что это решение имеет некоторые накладные расходы на вызовы виртуальных функций и тому подобное. Но это приемлемо.

Кроме того, может быть хорошей идеей объединить TypeTraits непосредственно в ваши структуры. Это приведет к меньшему количеству ввода, меньшему количеству кода, меньшим накладным расходам.

0 голосов
/ 07 января 2010

Звучит так, как будто Boost.Any может быть полезным, по крайней мере, чтобы понять, как удовлетворить ваши потребности.

0 голосов
/ 06 января 2010

В соответствии с вашими заявленными требованиями вам понадобится Factory , который создает нужные вам объекты. Factory может быть таким же простым, как std::map<*struct name*, *pointer to creation function*>. Однако вам также понадобится какой-то объект, содержащий операции, которые могут быть выполнены с загадочным объектом во время выполнения (это еще одна тема).

Чтобы Factory работал, вам нужно либо иметь все объекты, полученные из общего базового класса, либо использовать указатель void * для ссылки на них. (выглядит как общие методы программирования здесь ...)

Большинство фабричных шаблонов проектирования возвращают указатели на загадочные объекты . Возвращает указатель на экземпляр базового класса; но все, что вы знаете, это то, что он следует интерфейсу, определенному в базовом классе, таким образом, это загадочный объект. Оракул говорит, что вам нужно знать, какой объект генерирует фабрика, чтобы выполнять специальные действия над объектами. Большая часть вашей программы будет if object is *this* type, perform *special action*.

Как сказал Нейл, это то место, где начинается редизайн. Измените перспективу на точку зрения Объекта. Объект определяет все. Таким образом, методы должны принадлежать объекту так, чтобы он был самодостаточным. Например, как только Factory создаст объект, объект будет читать его данные и отображать аннотации и результаты. Если метод calculate является общим для всех объектов, то он становится чисто виртуальным методом базового класса, заставляя всех потомков иметь реализацию. Приятно то, что вы можете перебирать массив указателей на базовый класс и выполнять этот метод calculate без необходимости знать фактическую реализацию.

struct CommonBase
{
  virtual ResultType calculate(void) = 0;
};

struct Type1 : CommonBase
{
    int i;
    float f;
    ResultType calculate(void); // performs calculation using *i* and *f*
};

struct Type2{
    bool b1;
    bool b2;
    double d;
    ResultType calculate(void); // performs calculation using *b1*, *b2* and *d*
};

//...
std::vector<CommonBase *> the_objects;
//...
std::vector<CommonBase *>::iterator iter;
for (iter =  the_objects.begin();
     iter != the_objects.end();
     ++iter)
{
    ResultType result;
    result = (*iter)->calculate();
    std::cout << "Result: " << result << "\n";
}
0 голосов
/ 06 января 2010

На мой взгляд, лучше всего идти по маршруту C, а не C ++.

Вот как я бы решил вашу проблему, не зная больше о конкретном домене:

void *out_buf;

write_type(struct &a)
{
  out_buf = malloc(sizeof(a));
  memcpy(out_buf, a, sizeof(a)); 
}

write_type(struct &b); //so forth and so on. 

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

0 голосов
/ 06 января 2010

Пытаясь уточнить ответ drspod выше, допустим, вы создали следующий вспомогательный класс:

class HelperBase {
  virtual void *MakeArray(int n) = 0;
  virtual void *Get(void *array, int i) = 0;
}
template <typename T> class Helper {
  void *MakeArray(int n) { return new T[n]; }
  void *Get(void *array, int i) { return &(((T*)array)[i]); }
}

Теперь у вас есть карта от целых чисел (или флагов, или чего-то еще) до HelperBase s:

std::map<int, HelperBase*> GlobalMap;
GlobalMap[1] = new HelperBase<Type1>;
GlobalMap[2] = new HelperBase<Type2>;
// Don't forget to eventually delete these (or use smart pointers)

Теперь в время выполнения вы можете сказать

void *array = GlobalMap[someIndex].MakeArray(n);
void *thirdElement = GlobalMap[someIndex].Get(2);
0 голосов
/ 06 января 2010

Зачем вам они должны быть смежными в памяти?

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

  1. Создание классов - Почему вы используете структуры, а не классы? Я бы просто создал суперкласс для всех типов, которые вы хотите, подклассы для каждого другого типа, массив (или список, или коллекцию) указателей на объекты, и вы можете создать их во время выполнения.

  2. Решение с указателем структур - Если вам необходимо использовать структуры, вы можете создать структуру, которая имеет тег (для идентификации типа) и объединение указателей на различные структуры. Это было бы безопасно для доступа к типу, не требует приведения, а тег помог бы предотвратить ошибки кода. Это будет более эффективно использовать память, так как если вы создаете объединение фактических структур, вам придется выделить память для самой большой.

  3. Структурное решение - если вам действительно нужно, чтобы оно было непрерывным в памяти, создайте структуру с идентификатором тега и объединением различных структур, которые вам нужны.

В идеале, у вас будет суперкласс для всех типов, тогда вы

0 голосов
/ 06 января 2010

Как вы выбираете, какой тип использовать во время выполнения? Вы не упомянули об этом, и благодаря моим знаниям C ++ я не вижу простого способа сделать это вообще.

В любом случае, выбрав тип выбора во время выполнения, вы также можете определить его размер (используя sizeof) и, следовательно, создать массив:

size_t const size = sizeof(your_type);
char* buffer = new char[size * count];
your_type* = reinterpret_cast<your_type*>(buffer);

Теперь этот код работает, но совершенно бесполезен, потому что ему нужно знать тип как your_type, и тогда вы, конечно, можете просто сказать new your_type[count]. Однако вы можете создать сопоставление ваших типов с их размерами, используя предложенные вами целочисленные флаги:

enum type { tfoo, tbar };

std::map<type, size_t> type_sizes;

type_sizes[tfoo] = sizeof(foo);
type_sizes[tbar] = sizeof(bar);

// …

char* buffer = new char[type_sizes[type_index] * count];

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

PS: Поскольку вы хотите иметь такое поведение во время выполнения , метапрограммирование шаблона фактически не имеет к этому никакого отношения, а наоборот: метапрограммирование выполняется во время компиляции .

0 голосов
/ 06 января 2010

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

Что вы, вероятно, должны сделать, это самим управлять памятью. Создайте класс, который будет содержать число (количество элементов в массиве) и указатель char *. При желании иметь N структур типа T и массив int size[], дающий размеры для различных Ts, выделите память с new char(N * size[T]). Сохраните размер, и вы готовы к работе.

Чтобы получить доступ к памяти, вы можете использовать operator[] и вернуть void * или мастер-структуру, как описано ниже. (То, что вы возвращаете, должно быть указано во время компиляции, поэтому вы не можете использовать ни один из пятидесяти с лишним типов структуры.)

В этот момент вам нужно иметь функции, которые превращают необработанные байты в любые поля, которые вам нравятся, и наоборот. Они могут быть вызваны функцией operator[]. Вы не можете ничего сделать со структурой, тип которой не определен во время компиляции, поэтому вам, вероятно, понадобится основная структура с большим количеством полей, чтобы она могла обрабатывать все int s, все bool s, все float s, все double и т. д., которые будут иметь любые ваши структуры. Вам, конечно, понадобятся таблицы, чтобы показать, какие поля являются действительными.

Это большая работа, и я должен спросить, действительно ли это необходимо. Все это действительно поможет тебе купить что-нибудь полезное?

0 голосов
/ 06 января 2010

Вы можете использовать RTTI для записи оператора switch для принятия решений о типе объекта - во время выполнения.

Вы также можете использовать шаблон черт, чтобы назначить метаинформацию вашим типам: черты . После того, как вы создали общую черту и / или специализированные черты для ваших типов, вы можете обернуть контейнер std :: vector другим шаблоном для создания вашего объекта памяти. Однако вам придется создать 50 специализаций, поэтому постарайтесь ограничиться общей чертой.

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

0 голосов
/ 06 января 2010

Вы хотите, чтобы функция создала массив с шаблоном типа?

template <typename T>
T* makeArray( int n )
{
    return new T[n];
}

...

Type1* arrayoftype1 = makeArray<Type1>( 10 );
delete[] arrayoftype1;
...