0

好的,首先,性能在这里是最重要的,所以我怀疑地图是否可行。我有一个结构列表(大约 16 个),例如

struct A { ... };
struct B { ... }; 
...

每个都是不同的,每个都有不同的大小。

我想知道我们可以用什么优雅的方式来做一些事情:

char BufferA[sizeof(struct A)];
char BufferB[sizeof(struct B)];

然后编写一些方法或映射来返回 BufferA,如果你正在使用结构 A。速度绝对是最重要的,我想使用模板会有所帮助,但我不确定整个事情是否可以模板化。

更新***抱歉不清楚,缓冲区都是预先分配的。在给定结构类型的情况下,我只需要一种非常快速的方法来获得正确的缓冲区。

更新 2*** 抱歉没有指定,对齐是这里的一个重要特征,实际上我使用 #pragma pack(push, 1) 对每个结构进行字节对齐

4

7 回答 7

4
template<typename X>
struct Buffer
{
    static char *ptr()
    {
        // Note if no alignment is needed for your use then
        // just a simple "static char buf[sizeof(X)]; return buf;"
        // would be sufficient instead of the following.
        union Aligner {
            X x;
            char buf[sizeof(X)];
        };

        static Aligner a;

        return a.buf;
    }
};

struct B
{
    int x, y, z;
};

void foo()
{
    Buffer<B>::ptr()[2] = 12;
}

上面的代码只生成了g++ -O2一个固定的内存写入操作foo

.globl _Z3foov
    .type   _Z3foov, @function
_Z3foov:
.LFB1:
    .cfi_startproc
    .cfi_personality 0x0,__gxx_personality_v0
    pushl   %ebp
    .cfi_def_cfa_offset 8
    movl    %esp, %ebp
    .cfi_offset 5, -8
    .cfi_def_cfa_register 5

    movb    $12, _ZZN6BufferI1BE3ptrEvE1a+2   <=== this is the assignment

    popl    %ebp
    ret
    .cfi_endproc
于 2011-02-02T22:41:30.397 回答
1
char BufferA[sizeof(struct A)];

不保证自动字符数组正确对齐。(运算符 new(some_size) 和 new char[some_size] 保证对齐,但情况并非如此。)但是,您可以在 char 数组上使用编译器特定的对齐。

我想使用模板会有所帮助,但我不确定整个事情是否可以模板化。…我只需要一种非常快速的方法来获得给定结构类型的正确缓冲区。

由于这是基于类型的,因此模板是正确的方法。

template<class T>
struct Buffer {
  static char buffer[sizeof(T)] __attribute__((aligned));  // gcc's syntax
};

并且更方便地访问它,而不是 Buffer<A>::buffer:

template<class T>
inline
char* get_buffer() {
  return Buffer<T>::buffer;
}

void construct_example() {
  new (get_buffer<A>()) A();
  // same as:
  new (Buffer<A>::buffer) A();
}

这只允许每个结构类型有一个缓冲区——这可能是一个问题——但这似乎是你所期望和想要的。

于 2011-02-02T22:42:03.847 回答
0

这是剩下的东西,使用 boost fusion 容器,特别是map

#include <iostream>
#include <boost/fusion/container/map.hpp>
#include <boost/fusion/sequence/intrinsic/at_key.hpp>
#include <boost/array.hpp>

struct A{int a, b, c;};
struct B{double a, b, c;};
struct C{bool a, b, c; };

template <class T>
struct data
{
  data() : buffer() {}

  size_t size() const { return buffer.size(); }

  boost::array<char, sizeof(T)> buffer; // assuming no alignment issues!
};

int main(void)
{
  boost::fusion::map<boost::fusion::pair<A, data<A> >,
                     boost::fusion::pair<B, data<B> >,
                     boost::fusion::pair<C, data<C> >
    > buffer_holder;

  // to access
  std::cout << boost::fusion::at_key<A>(buffer_holder).size() << std::endl; // returns reference to data<A>

  return 0;
}

在这里,每种类型的所有缓冲区都归一个组件所有,并且每个缓冲区都经过正确构造并且可以由data包装器管理(如果您在多线程环境中很有用)。不是静态的...缺点是您可以使用的模板参数的数量是有限的,您可以使用编译器标志来增加它(我之前使用了多达 30 个)。

于 2011-02-02T23:07:15.593 回答
0

一个建议:改变设计。让结构返回指向其预期缓冲区的指针。

根据您的帖子,每个结构都有一个与之关联的缓冲区。这可以翻译为结构具有将返回关联缓冲区的方法

像这样的东西:

struct A
{
  char * get_buffer(void) const;
};

在实现文件中:

static char Buffer_A;  // Static to keep it private to this translation unit.
char * A::get_buffer(void) const
{
  return Buffer_A;
};

就执行而言,这是非常有效的。它也导致泛型。您可以在父类中放置一个纯虚拟抽象方法以返回缓冲区,从而在其他函数中处理对父类的指针或引用。

注意: 上面的实现使用在类外部声明的自动变量。在结构内声明的变量可以放在堆栈上,堆栈的大小限制可能比在类外声明的变量要小。更大的缓冲区也可以使用动态内存来声明。有关内存容量限制,请参阅编译器的文档。

于 2011-02-02T22:19:17.257 回答
0

如果您从中调用它的代码实际上具有类型A或其他类型的变量(而不是具有某种运行时开关),那么您可以在调用返回缓冲区的函数时将其用作模板参数。看起来像这样:

template <typename T>
char *makebuffer()
{
  return malloc(sizeof(T));
}

然后,在您的代码中,您编写makebuffer<A>()它,它将分配一个正确大小的缓冲区并返回它。

于 2011-02-02T22:05:08.247 回答
0

您可以编写一个具有数字模板参数和缓冲区静态成员的模板类。像这样的东西(未经测试):

template <size_t S>
class GlobalBuffer
   {
   static char Buffer[S];
   };

获取具有特定大小的结构的缓冲区现在可以这样编写:

GlobalBuffer<sizeof(struct A)>::Buffer;
于 2011-02-02T22:00:41.810 回答
0

尝试使用联合:在编译时解决,您只需对代码进行少量修改即可使用。

#include <iostream>

typedef struct A_
{
    int kk;
    long long pp;
};

//
// with this kind of struct, you have instant access to A contens without memory malllocs, memory leaks,
// you can use in multithread environments, ......
// you don't need virtual tables and so on. You can inherit.......
// you don't spend more memory yhan really necesary
// you don't spend time in functions calling ...
// 

typedef struct A
{
    union
    {
        A_ a;
        char buffer[sizeof(A_)];
    };
};

int main(int argc, char **argv)
{
    A a;

        // we fill the struct information
    a.a.kk = 12;
    a.a.pp = 99991829;

    A b;

        // we access to A buffer without problems
    memcpy(&b, a.buffer, sizeof(b));
    std::cout << "b.a.kk = " << b.a.kk << " b.a.pp = " << b.a.pp << std::endl;
}
于 2011-02-03T07:45:57.267 回答