views:

318

answers:

12

Hi! let's say i have a few different structure definitions (in fact, i have arround 50 such definitions):

struct Type1{
    int i;
    float f;
};

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

they are all POD, but can contain completely different data.

now, at runtime, i want to decide what type of those i need and then create an array (or vector) of this chosen type, such that all the data is layed out continguously in the memory.

How would I do that?

also - let's say i have an integer (it contains some flags) which determines what type of those structs i need. Is there any way how i could arrange the typedefinitions of those structs into a hashmap or so such that i can do only something like:

vector<myTypeHashMap[flagsInt]> myVect;

?

I know this goes rather thorwards metaprogramming (of which i have no clue :) ), but maybe there's a way to do that?

thanks

thanks

A: 

you could use a union struct and std::vector (note: this is an old-school c technique and is not meant for 50 objects :D). The unifying struct could like this:

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

If their isn't a large discrepancy in size isn't too large, this method allows you to mix and match types, or if you use only one type this mechanism will allow you to reuse the memory for different objects.

Hassan Syed
i don't get it - if i'd create an array of 'unifier' then each element would have the size of my largest struct, wouldn't it?
Mat
I don't think he wants the array to be able to contain all of the different types in different locations. He also said he had 50 types so a union would be a mess of a declaration. This solution also means that the array element size is determined by the largest struct, which is not memory efficient.
drspod
Yes, that is one of the strengths, or weaknesses depending on your problem.
Hassan Syed
@Mat: In this case yes
frunsi
weaknesses in my case - since i only want to have the data i'm actually using in this array, which is the whole point of why i have a lot of different types
Mat
You are looking at templates then :D
Hassan Syed
@Hassan Not if the type needs to be decided at run time.
anon
@neil thats when you mix runtime polymorphism with the the templates.
Hassan Syed
A: 

You can do something like this:



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


rossoft
yes - but as i mentioned, i have arround 50 such different tpyes. do i need to create typespecific code for each case?
Mat
You can automate that code generation with macros (possibly variadic, easy) or else you can dig into complex template metaprogramming (complex, for a hint there google for Alexandrescu's TypeList or Loki library and related articles)
David Rodríguez - dribeas
A: 

You want a function to create an array, templated by the type?

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

...

Type1* arrayoftype1 = makeArray<Type1>( 10 );
delete[] arrayoftype1;
drspod
What advantage does that have over simply using new?
anon
because he can use the pointer to a specific makeArray to switch on the type - as he states in the question, eg std::map<int, void*(int) >
drspod
hmmm - how would std::map go into that example? i was talking about the hashmap to store kind of the dynamic type, hashed by an integer, evaluated at runtime
Mat
Is a `T(*)(int)` really convertible to a `void(*)(int)`?
Rob Kennedy
@drspod: No he can't. Neil is correct there is no difference to just using new. Try and write some code that allows you to have a dynamic type at compiler time (its just not possible (well not like this)).
Martin York
A: 

You can use RTTI to write a switch statement to base decisions on the type of an object -- at runtime.

You can also use the trait pattern to assign meta-information to your types: traits. Once you have created the generic trait and/or the specialized traits for your types you can wrap the std::vector container with another template to create your contigious memory object. You would have to create 50 specializations though so try to restrict yourself to a generic trait.

I think sticking entirely to my first tip will give you better mileage as you don't want to instantiate 50 generic objects for your type.

Hassan Syed
i'm looking into that now, thanks, it appears to be somewhat related to my problem
Mat
hmm. but how would RTTI help me? it seems only to help me to identify the dynamic type of an object, but i don't see a way to create a new object of a type dynamically determined at runtime? also, in limitations it's stated that: RTTI can only be used with polymorphic types. "That means that your classes must have at least one virtual function, either directly or through inheritance. " my structs are POD types thus they must not contain a vtable pointer
Mat
That is correct, for RTTI you would need to use polymorphic types. templates for 50 types will introduce program bloat, and RTTI/union will create code bloat by hardwiring a switch statement and defining an enum for the 50 types.You said you wanted to make decisions at runtime to create objects and to lay them out in a contiguous fashion. None of the solutions possible with just the language are silver bullets. If you want an elegant solution than you need to generate the C++ using a code generator (google "imatix gsl"). or do everything purely at runtime (which I assume you know how to do).
Hassan Syed
If I was you I would merge the 50 types into a manageable number and then use the union trick I listed in another answer. I can't think of a problem that requires 50 different data structures, and if I did need to do that I would use a generative programming to maintain my sanity (IMATIX GSL).
Hassan Syed
what do you mean by 'doing everything purely at runtime' ?
Mat
+1  A: 

You could use some kind of factory. Search google for "factory pattern c++".

Some simple example code to explain it:

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 Example OOP-style mapping of TypeCode to some functionality and type description:

// 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();

This code was not tested, it may contain bugs ;)

Note, that this solution has some overhead of virtual function calls and the like. But its acceptable.

Also, it may be a good idea to merge TypeTraits directly into you structs. This will result in less typing, less code, less overhead.

frunsi
but here i still need type specific code for each of the 50 types :S
Mat
Yes, you will need it! If you give more details, then I can add a better example (using meta programming or similar). You mentioned an integer containing flags that describes the type? If so, you WILL NEED some kind of mapping between this integer and you structs.
frunsi
exactly - how could i define and access such a mapping? it's fine if i have to define this mapping by hand
Mat
I added an example for a mapping
frunsi
A: 

You aren't going to be able to do what you want while using compile-time tools like templates.

What you probably have to do is handle the memory yourself. Create a class that will hold a number (number of elements in the array), and a char * pointer. Given a desire for N structs of type T, and an int size[] array giving the sizes for the various Ts, allocate the memory with new char(N * size[T]). Store the size, and you're ready to go.

To access the memory, you can use operator[], and return a void *, or a master struct like outlined below. (What you return has to be specified at compile time, so you can't use any of the fifty-odd struct types.)

At that point, you need to have a functions that will turn raw bytes into whatever fields you like, and vice versa. These can be called by the operator[] function. You can't do anything with a struct that doesn't have a type determined at compile time, so you'll probably want a master struct with lots of fields, so it can handle all the ints, all the bools, all the floats, all the doubles and so forth any of your structs is going to have. You will of course need tables to show what fields are valid.

This is a whole lot of work, and I'd have to ask if it's really necessary. Is all this really going to buy you anything useful?

David Thornley
mmmh actually i'm trying to do something like that - but if i have such a 'masterstruct' - how would i convert the raw data to this struct type? i don't need to mark the valid fields, since only the ones which i stored effectively get accessed (i store only the ones who get accessed in the first place, and those will be the same which are accessed in a 'replay')
Mat
A: 

How do you select which type to use at runtime? You haven’t mentioned this and with my knowledge of C++ I don’t see an easy way to do this at all.

Anyway, once you’ve selected your choice type at runtime you can also determine its size (using sizeof) and consequently create an array:

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

Now, this code works, but is completely useless because it needs to know the type as your_type, and then you could of course just say new your_type[count]. You could, however, create a mapping of your types to their sizes, using the integer flags you proposed:

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

However, for practical solutions, you should rely on inheritance to compose a type hierarchy, and then perhaps a factory method as mentioned by others.

PS: Since you want to have this behaviour at runtime, template metaprogramming actually has got nothing to do with it, quite the opposite: metaprogramming is executed at compile time.

Konrad Rudolph
at runtime, a second subsystem processes the data. this subsystem is plugged together out of different subsystems (each time, the setup is different). each of those modules stores an intFlag describing which fields of the data it accesses. the whole subsystem at initiation simply OR's recursively all those flags of it's plugged modules to yeld an int describing what fields of the data will be accessed. by this int i want to select which type of struct to use
Mat
but this 'HOW' is the actual problem. i already thoght about this idea with the array of different sizes so at least the memory allocation would be simple - but then i still would have to map this memory to a specialised struct (or specialised memory access routine or so) in order to read and write the data
Mat
A: 

Why do you need them to be contiguous in memory?

I suspect that you would be better off simply creating an array of pointers, and then allocating the classes at runtime dynamically. Then you can set the pointers in the array to point to what you've created.

  1. Create Classes - Why are you using structs and not classes? I would just create a superclass for all the types you want, subclasses for each different type, an array (or list or collection) of pointers to the objects, and you can create them at runtime.

  2. Struct Pointer Solution - If you must use structs, you could create a struct that has a tag (to identify the type) and a union of pointers to the different structs. That would be type safe to access, no casting required, and the tag would help prevent code errors. This will be more memory efficient, as if you create a union of the actual structs, you will have to allocate memory for the largest one.

  3. Struct Solution - If you really do need it contiguous in memory, then create a struct with a tag id and a union of the different structs that you want.

Ideally, you would have a superclass for all the different types, then you

Larry Watanabe
A: 

Trying to flesh out drspod's answer above, let's say you make the following helper class:

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

Now you have a map from integers (or flags, or whatever) to HelperBases:

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)

Now at runtime you can say

void *array = GlobalMap[someIndex].MakeArray(n);
void *thirdElement = GlobalMap[someIndex].Get(2);
Roie Marianer
A: 

In my opinion, you are best off going down a C route instead of a C++ route.

This is how I would solve your problem, without knowing more about the specific domain:

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. 

I don't particularly know what you're trying to accomplish, so this may be a bit hard to fit in. At any rate, 50 types means a lot of code, whatever you do.

Paul Nathan
How would using C instead of C++ help the least bit here? Your code could be written much better in C++ (with templates). You don’t even need `malloc` and `memcpy`, instead you can use the approach showed by me, coupled with placement `new`.
Konrad Rudolph
Your approach is reasonably simple and readable: I rather like it. However, the hierarchy/factory-driven approach has a *significantly* higher LOC/complexity to accomplish the same goal, which is why I disagree with it and prefer the more C approach.
Paul Nathan
A: 

According to your stated requirements, you will need a Factory that creates the objects that you want. The Factory could be as simple as a std::map<*struct name*, *pointer to creation function*>. However, you will also need some kind of object containing operations that can be performed on the mystery object during run-time (which is another topic).

In order for the Factory to work, you either need to have all the objects derived from a common base class or use a void * pointer to refer to them. (Looking like generic programming techniques here...)

Most of the Factory design patterns return pointers to mystery objects. It returns a pointer to an instance of a base class; but all you know is that it follows the interface defined in the base class, thus it is a mystery object. The oracle says that you will need to know the kind of object the factory generates in order to perform special actions on the objects. Most of your program will be if object is *this* type, perform *special action*.

As Neil stated, this is where the re-design comes in. Change the perspective to the Object's point of view. The object determines everything. Thus the methods should belong to the object such that it is self-sufficient. For example, once the Factory creates an object, the object will read in its data and display annotations and results. If a calculate method is common to all objects, then it becomes a pure virtual method of the base class, forcing all descendants to have an implementation. The nice part is that you can iterate over an array of pointers to the base class and execute this calculate method without needing to know the actual implementation.

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";
}
Thomas Matthews
i can't really change the focus since the data to be recorded really is only data (POD's). it's the data which is provided by one subsystem and processed by another one. This data shall be recorded for a 'replay' - and in each 'processing' step only a small subset of the fields of such a data object are accessed (and only those need to be stored for a relay). the idea of the replay is that it should be performed in realtime.
Mat
A: 

It sounds like Boost.Any could be of potential use, at least to get an idea of how to satisfy your needs.

Void