Абсолютно быстрый (и, надеюсь, элегантный) способ вернуть определенный буфер символов заданного типа структуры - PullRequest
0 голосов
/ 03 февраля 2011

ОК, в первую очередь, здесь важнее всего производительность, поэтому я сомневаюсь, что карта будет работать.У меня есть список структур (около 16 из них), таких как

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

каждая отличается и каждая имеет разные размеры.

Мне интересно, каким элегантным способом мы могли бы быть в состояниисделайте что-то вроде:

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

, затем напишите какой-нибудь метод или отображение для возврата BufferA, если вы работаете со структурой A. Скорость, безусловно, является наиболее важной, я думаю, использование шаблонов поможет, но яне уверен, что все это может быть шаблонизировано.

Обновление *** Извините за неясность, все буферы предварительно выделены.Мне просто нужен очень быстрый способ получить правильный Buffer с заданным типом структуры.

Обновление 2 *** Извините за отсутствие указания, выравнивание является важной особенностью, и я на самом деле выполняю байтовое выравнивание каждой структуры с#pragma pack (push, 1)

Ответы [ 7 ]

4 голосов
/ 03 февраля 2011
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
1 голос
/ 03 февраля 2011
char BufferA[sizeof(struct A)];

Не гарантируется правильное выравнивание автоматических массивов символов.(Выравнивание гарантировано для оператора new (some_size) и нового char [some_size], но это не тот случай.) Однако вы можете использовать специфичное для компилятора выравнивание для массива char.

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

Поскольку это основано на типе, шаблон - правильный путь.

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

И получить к нему более удобный доступ, а не Buffer :: 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();
}

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

0 голосов
/ 03 февраля 2011

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

#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;
}
0 голосов
/ 03 февраля 2011

Здесь что-то осталось от поля, используйте контейнер слияния буста, в частности, 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 раньше).

0 голосов
/ 03 февраля 2011

Предложение: изменить дизайн. Пусть структуры возвращают указатель на свои предполагаемые буферы.

Согласно вашему сообщению, каждая структура имеет буфер , связанный с ней. Это может быть переведено как структура имеет метод, который будет возвращать связанный буфер .

Примерно так:

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

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

Примечание: Приведенная выше реализация использует автоматическую переменную, объявленную вне класса. Переменные, объявленные внутри структуры, могут быть помещены в стек , который может иметь меньшее ограничение по размеру, чем переменная, объявленная вне класса. Большие буферы также могут быть объявлены с использованием динамической памяти. См. Документацию вашего компилятора для ограничения объема памяти.

0 голосов
/ 03 февраля 2011

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

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

Затем в своем коде вы напишите makebuffer<A>(), и он выделит буфер правильного размера и вернет его.

0 голосов
/ 03 февраля 2011

Вы можете написать шаблонный класс, имеющий числовой параметр шаблона и статический член для буфера. Как то так (не проверено):

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

Получение буфера для структуры с определенным размером теперь можно записать так:

GlobalBuffer<sizeof(struct A)>::Buffer;
...