Шаблон C / C ++ для USE_HEAP или USE_STACK - PullRequest
3 голосов
/ 10 апреля 2009

Есть ли способ определения макроса (или чего-то подобного), который позволил бы точно размещать объекты в стеке или в куче?

например. Текущий код:

A a;
a.someFunc();

Самым простым предложением может быть следующее, но, как вы можете видеть ниже, не очень удобно поддерживать 2 набора кода.

#ifdef USE_STACK
  A a;
  a.someFunc();
#elseif USE_HEAP
  A* a = new A();
  a->someFunc();
#endif

Я ищу шаблон проектирования / прокси-класс, который можно использовать для компиляции кода в любом случае, в зависимости от потребностей нашего клиента.

Редактировать: Код используется для создания библиотеки для встроенного устройства / (встроенного) Linux / Windows Mobile. Большинству клиентов требуется только распределение на основе стека. Несколько других попросили обменять стек на кучу.

Спасибо, Charles

Ответы [ 5 ]

5 голосов
/ 10 апреля 2009

РЕДАКТИРОВАТЬ : улучшено, чтобы разрешить вызов функций-оболочек через operator->

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

#include <iostream>

#define USE_STACK

template <class T>
class HeapWrapper {
#ifdef USE_STACK
    T obj_;
#else
    T *obj_;
#endif
public:
#ifdef USE_STACK
    HeapWrapper() : obj_() {}

    template <class A1>
    HeapWrapper(const A1 &a1) : obj_(a1) {}

    template <class A1, class A2>
    HeapWrapper(const A1 &a1, const A2 &a2) : obj_(a1, a2) {}

    // etc

#else
    HeapWrapper() : obj_(new T()) {}
    ~HeapWrapper() { delete obj_; }

    template <class A1>
    HeapWrapper(const A1 &a1) : obj_(new T(a1)) {}

    template <class A1, class A2>
    HeapWrapper(const A1 &a1, const A2 &a2) : obj_(new T(a1, a2)) {}

    // etc
#endif

#ifdef USE_STACK
    operator const T &() const    { return obj_; }
    operator T &()                { return obj_; }
    T *operator->()               { return &obj_; }
    T& operator*()                { return obj_; }
#else
    operator const T &() const    { return *obj_; }
    operator T &()                { return *obj_; }
    T *operator->()               { return obj_; }
    T& operator*()                { return *obj_; }
#endif

    // cast operators makes this work nicely
    HeapWrapper &operator=(const T &rhs) { *obj_ = rhs; return *this;}
};


class A {
public:
    void member(int x) {
        std::cout << x << std::endl;
    }
};


int main() {
    HeapWrapper<int> x1(5);
    HeapWrapper<int> x2;
    HeapWrapper<int> x3 = x1;
    HeapWrapper<int> x4 = 3;

    std::cout << x1 << " " << x2 << " " << x3 << " " << x4 << std::endl;

    // example using a custom class's members..
    HeapWrapper<A> a1;
    a1->member(5);
}
2 голосов
/ 10 апреля 2009

Как-то так может помочь:

template <typename T>
class HeapWrapper
{
#ifdef USE_STACK
  T obj_;
#else
  T *obj_;
#endif
public:
#ifdef USE_STACK
  HeapWrapper() : obj_() {}
#else
  HeapWrapper() : obj_(new T()) {}
#endif

#ifdef USE_STACK
  const T& obj() const
  { return obj_; }

  T& obj() const
  { return obj_; }
#else
  const T& obj() const
  { return *obj_; }

  T& obj() const
  { return *obj_; }
#endif
};

Однако обратите внимание, что это либо ограничивает вас объектами только с конструктором по умолчанию. Обернутый класс может предоставить функцию Init(...), которая может быть передана функцией шаблона variadic классом-оболочкой (или просто добавить template <typename T1, template T2, [etc]> Init(const T1 &x1, cons tT2 &x2) для каждой необходимой вам арности):

template <typename T1>
void Init(const T1& x1)
{
#ifdef USE_STACK
  obj_.Init(x1);
#else
  obj_->Init(x1);
#endif
}

template <typename T1, typename T2>
void Init(const T1& x1, const T2& x2)
{
#ifdef USE_STACK
  obj_.Init(x1, x2);
#else
  obj_->Init(x1, x2);
#endif
}

Бонусные баллы, если ваш компилятор имеет вариативных шаблонов уже:

template<typename... T>
void foo(const T&... values) {
#ifdef USE_STACK
  obj_.Init(values...);
#else
  obj_->Init(values...);
#endif
}
2 голосов
/ 10 апреля 2009

Если вы задаете этот вопрос, я думаю, вы должны начать изучать пользовательский распределитель. Для начала, если вы выделите большой блок памяти, вы можете использовать его как стек или кучу, изменяя внутренние компоненты распределителя. Вы должны распределить все объекты с помощью умного указателя (чтобы предотвратить утечки памяти), и вся память будет передана вам из вашей пользовательской кучи.

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

Ваш код всегда будет использовать создание объектов в стиле «куча», поэтому вам потребуется только 1 способ кодирования и вам не понадобятся условные макросы. Проверьте распределители блоков (которые создают несколько куч блоков фиксированного размера, вы даете первому свободному блоку следующего размера до вызывающего (например, блоки имеют размер 16 байтов, 32 байта, 64 байта и т. Д.), Это очень быстро выделить и освободить, хотя это неэффективно с использованием памяти).

1 голос
/ 10 апреля 2009

Обычно размер стека ограничен, поэтому любые большие объекты в любом случае должны быть распределены в куче.

Все экземпляры, которые реализуют идиому защиты RAII (например, для получения и освобождения мьютекса), должны быть помещены в стек, чтобы они очищались, если вы покидаете контекст (например, путем возврата или исключения)

Идиома, которая позволяет вам переключаться, как правило, не очень полезна, так как необходимо принять осторожное решение, как погода, чтобы поместить объекты в стек или в кучу. Обычно та или иная проблема лучше подходит для решения проблемы, и переключение не имеет смысла.

0 голосов
/ 10 апреля 2009

Не окончательный ответ, но я надеюсь, что это может помочь:

#ifdef USE_STACK
  A a;
#elseif USE_HEAP
  auto_ptr<A> CAUTION_USE_OF_AUTOPTR_a( new A() );
  A& a = CAUTION_USE_OF_AUTOPTR_a.get();
#endif
a.someFunc();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...