Мне нужно реализовать контейнер для хранения некоторого количества элементов, и по какой-то причине он должен работать без выделения кучи. Другое требование состоит в том, что элементы контейнера нельзя копировать или перемещать каким-либо образом. Они должны быть встроены непосредственно в память, выделенную контейнером.
Для этого я решил использовать новое размещение и полностью делегировать управление памятью реализации контейнера (нашел полезную информацию о новом размещении на drdobbs).
Рабочий пример можно найти здесь. (Обратите внимание, что использование new uint8_t[size]
и std::queue
сделано только для того, чтобы сделать пример простым. Вместо этого мой реальный код имеет более сложную реализацию без кучи.)
Это прекрасно работает до сих пор, так как клиентский код должен помещать элементы в контейнер с такими вызовами, как:
executer.push(new (executer) MyRunnable("Hello", 123));
Теперь я хочу устранить необходимость повторной записи executer
в этом заявлении. Я бы предпочел написать что-то вроде, например:
executer.pushNew(MyRunnable("Hello", 123));
or
executer.pushNew(MyRunnable, "Hello", 123);
возможно, предоставив соответствующий шаблон, но я не смог его написать (без макросов препроцессора, пожалуйста).
Я нашел полезную информацию о std::allocator
здесь по адресу drdobbs но не знаю, как это применить к моей задаче (к тому же, статья датирована 2000 годом, поэтому не пользуйтесь возможными преимуществами C++11).
Может ли кто-нибудь помочь мне найти способ больше не давать executer
дважды?
Редактировать: после успешного утверждения ответа Jarod42 я обновил свой рабочий код примера здесь.
И для истории, здесь исходный пример кода моего первоначального вопроса:
#include <iostream>
#include <queue>
class Runnable {
// Runnable should be uncopyable and also unmovable
Runnable(const Runnable&) = delete;
Runnable& operator = (const Runnable&) = delete;
Runnable(const Runnable&&) = delete;
Runnable& operator = (const Runnable&&) = delete;
public:
explicit Runnable() {}
virtual ~Runnable() {}
virtual void run() = 0;
};
class MyRunnable: public Runnable {
public:
explicit MyRunnable(const char* name, int num): name(name), num(num) {}
virtual void run() override {
std::cout << name << " " << num << std::endl;
}
private:
const char* name;
int num;
};
class Executer {
// Executer should be uncopyable and also unmovable
Executer(const Executer&) = delete;
Executer& operator = (const Executer&) = delete;
Executer(const Executer&&) = delete;
Executer& operator = (const Executer&&) = delete;
public:
explicit Executer() {
}
void* allocateEntry(size_t size) {
// this heap allocation is just to keep this example simple
// my real implementation uses it's own memory management instead (blockpool)
return new uint8_t[size];
}
void push(Runnable* entry) {
queue.push(entry);
}
template <typename R> // this don't works
void pushNew(R) {
push(new (*this) R);
}
inline friend void* operator new(size_t n, Executer& executer) {
return executer.allocateEntry(n);
}
void execute() {
while (queue.size() > 0) {
Runnable* entry = queue.front();
queue.pop();
entry->run();
// Now doing "placement delete"
entry->~Runnable();
uint8_t* p = reinterpret_cast<uint8_t*>(entry);
delete[] p;
}
}
private:
// this use of std::queue is just to keep this example simple
// my real implementation uses it's own heap-less queue instead
std::queue<Runnable*> queue {};
};
int main() {
Executer executer;
executer.push(new (executer) MyRunnable("First", 1));
executer.push(new (executer) MyRunnable("Second", 2));
executer.push(new (executer) MyRunnable("Third", 3));
// but want to use it more like one this
//executer.pushNew(MyRunnable("Fifth", 5)); // how to implement it?
//executer.pushNew(MyRunnable, "Sixth", 6); // or maybe for this usage?
executer.execute();
}
std::array
? - person Vaughn Cato   schedule 03.10.2015std::array
использовать нельзя. Кроме того, способ управления памятью контейнером должен быть полностью закрытым. - person Joe   schedule 03.10.2015MyRunnable
вpush()
, не могли бы вы вместо этого передать аргументы (name
иnum
)? Тогда у вас может бытьnew
внутриpush()
. - person Vaughn Cato   schedule 03.10.2015MyRunnable
ctor неизвестны контейнеру, и могут бытьFooRunnable
иBarRunnable
даже с разными параметрами. Звучит как необходимость использовать шаблон с переменным числом аргументов… - person Joe   schedule 03.10.2015