8

I need to implement a container to hold an amount of elements and for some reason, it has to work without any heap allocation. Another requirement is, that the container elements should not be copied or moved in any way. They have to constructed directly into the memory allocated by the container.

For that, I decided to use placement new and delegate the memory management completely to the container implementation (found some useful information about placement new at drdobbs).

A running example is found here. (Please note, that the use of new uint8_t[size] and std::queue is just to keep the example simple. My real code has more complex, heap-less implementation instead.)

This perfectly works so far, as the client code has to put elements into the container with calls like:

executer.push(new (executer) MyRunnable("Hello", 123));

Now I want do remove the need of the repeated write executer in this statement. I would rather like to write something like e.g.:

executer.pushNew(MyRunnable("Hello", 123));

or

executer.pushNew(MyRunnable, "Hello", 123);

maybe by providing an appropriate template but I failed to write one (no preprocessor macros, please).

I'd found some useful information about std::allocator here at drdobbs but don't know how to apply it to my problem (further, the article is of anno 2000 and so don't take use of possible C++11 advantages).

Could one help me to find a way to not longer need to give the executer twice?

Edit: After successful approving Jarod42's answer, I'd updated my running example code here.

And for the history, here the original example code of my initial question:

#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();
}
Community
  • 1
  • 1
Joe
  • 3,090
  • 6
  • 37
  • 55
  • Are the number of elements known at compile time? Could you use `std::array`? – Vaughn Cato Oct 03 '15 at 18:38
  • 1
    No, `std::array` cannot be used. Further, the way how the container manages the memory has to be completely private. – Joe Oct 03 '15 at 18:42
  • Have you looked at std::vectors implementation of emplace back? Sounds like this is exactly what you need (ignoring the haep vs stack issue, which is a sepatate concerne) – MikeMB Oct 03 '15 at 18:47
  • Come to think of it, you could probably even build a wrapper around std::vector with a stack allocator – MikeMB Oct 03 '15 at 18:50
  • Instead of passing a `MyRunnable` to `push()`, could you pass the arguments (`name` and `num`) instead? Then you could have the `new` inside the `push()`. – Vaughn Cato Oct 03 '15 at 18:51
  • I think I have done the same thing before (in AddNew function): https://bitbucket.org/darkgazeorg/gorgon-game-engine/src/a1c8c5fbcd51b734c6ab6234e0287e05515dc5d9/Source/Gorgon/Containers/Collection.h?at=4.x-dev&fileviewer=file-view-default – Cem Kalyoncu Oct 03 '15 at 19:05
  • The `MyRunnable` ctor arguments are not known by the container and there might be a `FooRunnable` and a `BarRunnable` with even different parameters. Sounds a bit like have to use variadic template … – Joe Oct 03 '15 at 19:06
  • this question is possibly a duplicate of : http://stackoverflow.com/questions/28628484/custom-container-emplace-with-variadic-templates you are asking for "emplace" functionality wich is inserted by design (at least in C++11, I start to forget about old C++ xD) – CoffeDeveloper Oct 04 '15 at 13:18
  • You're right, my question is exactly about »emplace« functionality (and now, I'll rename the member function in my code. For this, it might duplicate. But I think that the answers of Jarod42 and Barry are really beautiful and clear. Finally I'd added »emplace« to the questions subject. – Joe Oct 04 '15 at 22:02

2 Answers2

27

There are two things wrong with this:

template <typename R> // this don't works
void pushNew(R) {
    push(new (*this) R);
}

The first is answered by Jarod42 in that you want to do:

template <typename R, typename... Ts>
void pushNew(Ts&&... args) {
    push(new (*this) R(std::forward<Ts>(args)...));
}

but even more importantly... new (*this) R is really bizarre. It looks like you're constructing an R over yourself! But you're not, you're just using that syntax to call your allocator. That horribly violates the principle of least surprise. It took me quite a while to understand what was going on.

What you should to is just use your allocator directly:

template <typename R, typename... Ts>
void pushNew(Ts&&... args) {
    void* slot = allocateEntry(sizeof(R));
    push(new (slot) R(std::forward<Ts>(args)...));
}

That is a lot easier to understand.

Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
14

With:

template <typename R, typename... Ts>
void pushNew(Ts&&... args) {
    push(new (*this) R(std::forward<Ts>(args)...));
}

You can write:

executor.PushNew<MyRunnable>("Hello", 123);

instead of

executer.push(new (executer) MyRunnable("Hello", 123));
Jarod42
  • 203,559
  • 14
  • 181
  • 302