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

发布于 2024-08-16 14:34:24 字数 508 浏览 5 评论 0原文

假设我有一些不同的结构定义(事实上,我有大约 50 个这样的定义):

struct Type1{
    int i;
    float f;
};

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

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

现在,在运行时,我想决定我需要什么类型,然后创建该所选类型的数组(或向量),以便所有数据连续布置在内存中。

我该怎么做呢?

另外 - 假设我有一个整数(它包含一些标志),它确定我需要什么类型的结构。有什么方法可以将这些结构的类型定义安排到哈希映射中,这样我只能做类似的事情:

vector<myTypeHashMap[flagsInt]> myVect;

我知道这非常适合元编程(我对此一无所知:)),但也许有办法做到这一点?

谢谢

谢谢

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

稀香 2024-08-23 14:34:24

你可以使用某种工厂。在 google 中搜索“工厂模式 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 直接合并到结构中可能是个好主意。这将导致更少的打字、更少的代码和更少的开销。

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.

北座城市 2024-08-23 14:34:24

你可以这样做:



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


You can do something like this:



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


︶葆Ⅱㄣ 2024-08-23 14:34:24

您想要一个函数来创建一个由类型模板化的数组?

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

...

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

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;
与之呼应 2024-08-23 14:34:24

您可以使用 RTTI 编写 switch 语句来根据对象的类型——运行时。

您还可以使用特征模式将元信息分配给您的类型:特征。一旦为您的类型创建了通用特征和/或专用特征,您就可以用另一个模板包装 std::vector 容器来创建连续的内存对象。不过,您必须创建 50 个专业化,因此请尝试将自己限制在通用特征上。

我认为完全遵循我的第一个技巧会给您带来更好的效果,因为您不想为您的类型实例化 50 个通用对象。

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.

黑寡妇 2024-08-23 14:34:24

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

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

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

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.

汐鸠 2024-08-23 14:34:24

使用模板等编译时工具时,您将无法做您想做的事情。

您可能需要做的是自己处理内存。创建一个类,该类将保存一个数字(数组中的元素数量)和一个 char * 指针。假设需要 N 个 T 类型的结构,以及一个给出各种 T 大小的 int size[] 数组,请使用 new char(N * size[T])< 分配内存/代码>。存储尺寸,然后就可以开始了。

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

此时,您需要有一个函数可以将原始字节转换为您喜欢的任何字段,并且反之亦然。这些可以由 operator[] 函数调用。您无法对编译时未确定类型的结构执行任何操作,因此您可能需要一个包含大量字段的主结构,以便它可以处理所有 int 、所有 bool 、所有 float 、所有 double 等任何结构都将具有。您当然需要表格来显示哪些字段有效。

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

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?

清音悠歌 2024-08-23 14:34:24

如何选择在运行时使用哪种类型?你还没有提到这一点,以我对 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:由于您希望在运行时有这种行为,模板元编程实际上与它无关,恰恰相反:元编程是在编译时执行的。

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.

戴着白色围巾的女孩 2024-08-23 14:34:24

为什么需要它们在内存中连续?

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

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

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

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

    结构

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

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

じ违心 2024-08-23 14:34:24

尝试充实上面 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]); }
}

现在您有一个从整数(或标志或其他)到 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)

现在在运行时你可以说

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

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);
我是有多爱你 2024-08-23 14:34:24

在我看来,你最好走 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 种类型意味着很多代码,无论你做什么。

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.

沦落红尘 2024-08-23 14:34:24

根据您声明的要求,您将需要一个工厂来创建您想要的对象。 工厂可以像std::map<*结构名称*,*指向创建函数的指针*>一样简单。但是,您还需要某种包含可以在运行时对神秘对象执行的操作的对象(这是另一个主题)。

为了让工厂工作,您要么需要从公共基类派生所有对象,要么使用 void * 指针来引用它们。 (这里看起来像通用编程技术...)

大多数工厂设计模式都会返回指向神秘对象的指针。它返回一个指向基类实例的指针;但你所知道的是它遵循基类中定义的接口,因此它是一个神秘的对象。预言说你需要知道工厂生成的对象类型,以便对对象执行特殊操作。您的程序的大部分内容将是如果对象是*此*类型,则执行*特殊操作*

正如尼尔所说,这就是重新设计的切入点。将视角更改为对象的观点。对象决定一切。因此,方法应该属于对象,以便它是自给自足的。例如,一旦Factory创建了一个对象,该对象将读取其数据并显示注释和结果。如果一个计算方法对于所有对象都是通用的,那么它就成为基类的纯虚方法,迫使所有后代都有一个实现。好的部分是,您可以迭代指向基类的指针数组并执行此 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";
}

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";
}
莫相离 2024-08-23 14:34:24

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文