c ++创建连续的类型数组,这在编译时是未知的

时间:2010-01-06 13:26:05

标签: c++ metaprogramming dynamic-data

让我说我有一些不同的结构定义(事实上,我有50个这样的定义):

struct Type1{
    int i;
    float f;
};

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

它们都是POD,但可以包含完全不同的数据。

现在,在运行时,我想确定我需要的那种类型,然后创建一个这个所选类型的数组(或向量),这样所有的数据都会在内存中熄灭。

我该怎么做?

也 - 假设我有一个整数(它包含一些标志),它决定了我需要的结构类型。有没有什么方法可以将这些结构的类型定义排列成一个哈希映射,这样我就可以只执行以下操作:

vector<myTypeHashMap[flagsInt]> myVect;

我知道这是相当深入的元编程(其中我不知道:)),但也许有办法做到这一点?

感谢

感谢

12 个答案:

答案 0 :(得分:1)

你可以使用某种工厂。在谷歌搜索“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];
  ...
  }
}

编辑 TypeCode到某些功能和类型描述的示例OOP样式映射:

// 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直接合并到结构中可能是个好主意。这样可以减少打字,减少代码,减少开销。

答案 1 :(得分:0)

你可以使用一个union结构和std :: vector(注意:这是一个老式的c技术,并不适用于50个对象:D)。统一结构可能是这样的:

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

如果它们的大小差别不是太大,则此方法允许您混合和匹配类型,或者如果您只使用一种类型,则此机制将允许您将内存重用于不同的对象。

答案 2 :(得分:0)

您可以这样做:



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


答案 3 :(得分:0)

你想要一个函数来创建一个由类型?

模板化的数组
template <typename T>
T* makeArray( int n )
{
    return new T[n];
}

...

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

答案 4 :(得分:0)

您可以使用RTTI编写switch语句,以便在运行时根据对象类型做出决定。

您还可以使用特征模式为元素分配元信息:traits。一旦为类型创建了通用特征和/或特殊特征,就可以将std :: vector容器与另一个模板包装在一起,以创建一个有名的内存对象。你必须创建50个特化,所以试着限制自己的一般特征。

我认为完全坚持我的第一个提示会给你更好的里程,因为你不想为你的类型实例化50个通用对象。

答案 5 :(得分:0)

使用像模板这样的编译时工具时,你无法做你想做的事。

您可能需要做的就是自己处理内存。创建一个包含数字(数组中的元素数)和char *指针的类。鉴于需要T类型的N个结构,并且int size[]数组给出各种Ts的大小,请使用new char(N * size[T])分配内存。存储大小,你准备好了。

要访问内存,您可以使用operator[],并返回void *或主结构,如下所述。 (您返回的内容必须在编译时指定,因此您不能使用任何五十多种结构类型。)

此时,您需要具有将原始字节转换为您喜欢的任何字段的函数,反之亦然。这些可以由operator[]函数调用。对于没有在编译时确定类型的结构,你不能做任何事情,所以你可能想要一个包含很多字段的主结构,所以它可以处理所有的int,所有的bool s,所有float s,所有double s等所有结构都将具有。您当然需要表格来显示哪些字段有效。

这是一项繁重的工作,我不得不问这是否真的有必要。这真的会给你带来什么有用的东西吗?

答案 6 :(得分: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:由于你想在运行时这个行为,模板元编程实际上与它无关,恰恰相反:元编程在编译时执行

答案 7 :(得分:0)

为什么你需要它们在记忆中连续?

我怀疑你最好只创建一个指针数组,然后动态地在运行时分配类。然后,您可以设置数组中的指针以指向您创建的内容。

  1. 创建类 - 为什么使用结构而不是类?我只想为你想要的所有类型创建一个超类,为每个不同类型创建一个子类,一个指向对象的指针的数组(或列表或集合),你可以在运行时创建它们。

  2. 结构指针解决方案 - 如果必须使用结构,则可以创建一个结构,该结构具有标记(用于标识类型)和指向不同结构的指针的并集。这将是类型安全的访问,不需要强制转换,标记将有助于防止代码错误。这将提高内存效率,就像您创建实际结构的并集一样,您必须为最大的结构分配内存。

  3. 结构解决方案 - 如果你确实需要它在内存中连续,那么创建一个带有标签id和你想要的不同结构的联合的结构。

  4. 理想情况下,您将拥有所有不同类型的超类,然后

答案 8 :(得分: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的映射:

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

答案 9 :(得分: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种类型意味着很多代码,无论你做什么。

答案 10 :(得分:0)

根据您声明的要求,您需要一个 Factory 来创建您想要的对象。 Factory 可以像std::map<*struct name*, *pointer to creation function*>一样简单。但是,您还需要某种对象,其中包含可在运行时在神秘对象上执行的操作(这是另一个主题)。

为了使 Factory 工作,您需要拥有从公共基类派生的所有对象,或者使用void *指针来引用它们。 (在这里看起来像泛型编程技术......)

大多数 Factory 设计模式返回指向神秘对象的指针。它返回一个指向基类实例的指针;但你所知道的是它遵循基类中定义的接口,因此它是一个神秘的对象。 oracle说你需要知道工厂生成的对象类型,以便对对象执行特殊操作。您的大多数计划都是if object is *this* type, perform *special action*

正如Neil所说,这是重新设计的地方。将视角更改为Object的观点。对象决定了一切。因此,这些方法应属于对象,使其自给自足。例如,一旦 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";
}

答案 11 :(得分:0)

听起来Boost.Any可能具有潜在用途,至少可以了解如何满足您的需求。