2010-01-06 1 views
0

скажем у меня есть несколько различных определений структуры (на самом деле, у меня есть Arround 50 таких определений):C++ создать continguous массив типа, который не известен на compiletime

struct Type1{ 
    int i; 
    float f; 
}; 

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

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

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

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

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

vector<myTypeHashMap[flagsInt]> myVect; 

?

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

благодарит

благодарит

+0

Похоже, вам понадобится шаблон фабричного дизайна. – Dmitry

+0

Теперь, когда я думаю об этом, даже если вам удастся создать массив любого из этих типов, использование такого разнообразного зоопарка типа будет PITA. Нет ли способа упростить дизайн? – Dmitry

+8

Наличие 50 несвязанных типов звучит как довольно сомнительный дизайн для меня. – 2010-01-06 13:43:17

ответ

0

вы могли бы использовать объединение структуры и станд :: вектор (примечание: это метод старой школы с и не предназначена для 50 объектов: D). Объединяющая структура может нравится:

struct unifier 
{ 
    int type; 
    union 
    { 
     struct A; 
     struct B; 
    }; 
}; 

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

+0

Я не понимаю - если бы я создал массив «унификатора», то каждый элемент имел бы размер моей самой большой структуры, не так ли? – Mat

+0

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

+0

Да, это одна из сильных сторон или слабостей в зависимости от вашей проблемы. –

0

Вы можете сделать что-то вроде этого:



void * buffer; 
if (a) { 
    buffer = new Type1[30]; 
} else { 
    buffer = new Type2[30]; 
} 


+0

да - но, как я уже упоминал, у меня есть около 50 таких разных tpyes. мне нужно создать спецификационный код для каждого случая? – Mat

+0

Вы можете автоматизировать создание этого кода с помощью макросов (возможно, вариабельных, простых), иначе вы можете вникать в сложное метапрограммирование шаблонов (сложный, для подсказки там google для Alexandrescu TypeList или библиотеки Loki и связанных с ним статей) –

0

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

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

... 

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

Какое преимущество имеет это просто используя новый? – 2010-01-06 13:40:07

+0

, потому что он может использовать указатель на определенный makeArray для включения типа - как он заявляет в вопросе, например std :: map

+0

hmmm - как бы std :: map перейти в этот пример? я говорил о hashmap для хранения типа динамического типа, хэшированного целым числом, оцененного во время выполнения. – Mat

0

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

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

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

+0

Я смотрю на это сейчас, спасибо, похоже, это связано с моей проблемой – Mat

+0

hmm. но как мне помочь RTTI? это только помогает мне идентифицировать динамический тип объекта, но я не вижу способа создать новый объект типа, динамически определяемого во время выполнения? Кроме того, в ограничениях указано, что: RTTI может использоваться только с полиморфными типами. «Это означает, что ваши классы должны иметь как минимум одну виртуальную функцию, либо напрямую, либо через наследование». Мои структуры являются типами POD, поэтому они не должны содержать указатель vtable. – Mat

+0

Это верно, для RTTI вам необходимо использовать полиморфные типы. шаблоны для 50 типов будут внедрять разбухание программы, а RTTI/union создаст раздувание кода, проводя инструкцию switch и определяя перечисление для 50 типов. Вы сказали, что хотите принять решения во время выполнения, чтобы создавать объекты и выкладывать их смежным образом. Ни одно из решений, доступных только с языком, это серебряные пули. Если вам требуется элегантное решение, то вам нужно сгенерировать C++ с помощью генератора кода (google «imatix gsl»). или делать все чисто во время выполнения (что, я полагаю, вы знаете, как это сделать). –

1

Вы можете использовать какой-то завод. Найдите google для «factory pattern C++».

Некоторые простой пример кода, чтобы объяснить:

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

EDIT Пример отображения OOP-стиля 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(); 

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

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

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

+0

, но здесь мне все еще нужен код типа для каждого из 50 типов: S – Mat

+0

Да, он вам понадобится! Если вы дадите более подробную информацию, я могу добавить лучший пример (используя метапрограммирование или подобное). Вы упомянули целое число, содержащее флаги, описывающие тип? Если это так, вам нужно какое-то сопоставление между этим целым и вашими структурами. – Frunsi

+0

точно - как я могу определить и получить доступ к такому отображению? это нормально, если я должен определить это сопоставление вручную – Mat

0

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

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

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

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

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

+0

mmmh на самом деле я пытаюсь сделать что-то подобное, но если у меня есть такая «мастер-структура» - как бы я преобразовал необработанные данные в этот тип структуры? мне не нужно отмечать допустимые поля, так как только те, которые я хранил, эффективно получают доступ (я сохраняю только тех, кто получает доступ в первую очередь, и те будут теми же, к которым обращаются в «повтор») – Mat

0

Как выбрать тип, который будет использоваться во время выполнения? Вы не упомянули об этом и, насколько мне известно, 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

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

+0

, но это «КАК» является реальной проблемой.я уже обсуждал эту идею с массивом разных размеров, поэтому, по крайней мере, распределение памяти было бы простым - но тогда мне все равно пришлось бы сопоставлять эту память с специализированной структурой (или специализированной процедурой доступа к памяти или так), чтобы читать и напишите данные – Mat

0

Зачем вам нужно, чтобы они были непрерывными в памяти?

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

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

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

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

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

0

Попытки конкретизировать ответ 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:

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

void *array = GlobalMap[someIndex].MakeArray(n); 
void *thirdElement = GlobalMap[someIndex].Get(2); 
0

По-моему, вы лучше от спускаясь 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

Как использовать здесь C вместо C++? Ваш код может быть написан гораздо лучше на C++ (с шаблонами). Вам даже не нужны 'malloc' и' memcpy', вместо этого вы можете использовать предложенный мной подход в сочетании с размещением 'new'. –

+0

Ваш подход достаточно прост и читабельен: мне это нравится. Тем не менее, иерархический/основанный на заводе подход имеет * значительно более высокую LOC/сложность для достижения одной и той же цели, поэтому я не согласен с этим и предпочитаю более подход C. –

0

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

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

Большинство Factory шаблонов проектирования возвращают указатели на объекты тайны. Он возвращает указатель на экземпляр базового класса; но все, что вы знаете, это то, что он следует интерфейсу, определенному в базовом классе, таким образом, он является загадочным объектом. Оракул говорит, что вам нужно знать тип объекта, который генерирует завод, для выполнения специальных действий над объектами. Большая часть вашей программы будет 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

Я не могу изменить фокус, так как данные, которые будут записаны, действительно являются только данными (POD). это данные, которые предоставляются одной подсистемой и обрабатываются другой. Эти данные должны записываться для «повтора», и на каждом этапе «обработки» осуществляется доступ только к небольшому подмножеству полей такого объекта данных (и только те, которые необходимо сохранить для реле). идея повтора заключается в том, что он должен выполняться в реальном времени. – Mat

0

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

Смежные вопросы