使用没有堆内存分配的接口的框架应用程序?

Framework applications using interface without heap memory allocation?

我正在尝试为要在我的微处理器中使用的应用程序创建一个框架。我正在使用 Arduino IDE 来编译和部署程序。

由于微处理器的堆内存通常较低,因此我想尽可能只使用堆栈内存。

最小示例:

完整示例代码可见here.

我会描述我认为最有趣的部分。


iMinExApplication(接口):

class iMinExApplication 
{
    public:
        virtual void initialize() = 0; // pure virtual
        virtual void execute()    = 0; // pure virtual

        virtual ~iMinExApplication() = default; // Virtual destructor
};

tMinExApplication(接口的扩展,仅供框架使用):

class tMinExApplication
{
    public:
        ...
        tMinExApplication(iMinExApplication* app, const char name[]) : App(app)
        {
            strcpy(Name, name);
        };
        ...
        void execute()    { App->execute(); };

    private:
        iMinExApplication* App;
        char Name[32]; 
};

tMinExCoordinator(master,调用添加的应用)

class tMinExCoordinator
{
    public:
        ...
        void addApp(iMinExApplication* app, const char name[]) 
        {
            tMinExApplication* tmpPtr = new tMinExApplication(app, name); // HERE!
            Applications[++NumApps] = tmpPtr;
            tmpPtr = nullptr;
        };
        ...
        void runApps()
        {
            for (auto& app : Applications) {
                // Frequency check
                // ...
                app->execute();
            }
        };

    private:
        tMinExApplication* Applications[];
        int NumApps;
};

tMyApp(用户自定义应用,使用继承接口)

class tMyApp : public iMinExApplication {
  ...

minExSketch(Arduino IDE 草图)

#include "tMinExClasses.hpp"
#include "tMyApp.hpp"

tMinExCoordinator coordinator{};
tMyApp tmpApp{};

void setup() {
  Serial.begin(9600);
  coordinator.addApp(&tmpApp, "TEST");
  coordinator.initializeApps();
}

void loop() {
  coordinator.runApps();
}

以上作品。但是应用程序是在堆内存中分配的,因为它使用关键字 new'HERE!'tMinExCoordinator class 定义中,第 57 行在 tMinExClasses.hpp).

没有它我似乎无法让它工作。 除了在堆栈内存中分配内存外,我还可以通过其他什么方式实现它?

要求:

我知道智能指针,但不确定它们是否使用堆内存。另外,我希望最小示例尽可能简洁。

I cannot seem to get it to work without it. In what other way could I implement this, but only allocating memory in stack memory?*

您可以预先分配一个足够大小的字节数组,然后使用 placement-new to construct objects inside of that array (see std::aligned_storage 来帮助您。)多态性只需要 pointers/references 在运行时工作,不需要动态分配。

template<std::size_t MaxApps>
class tMinExCoordinator
{
    public:
        ...

        tMinExCoordinator()
        {
            Applications = reinterpret_cast<tMinExApplication*>(appBuffer);
        }

        ~tMinExCoordinator()
        {
            for (std::size_t i = 0; i < NumApps; ++i)
                Applications[i].~tMinExApplication();
        }

        void addApp(iMinExApplication* app, const char name[]) 
        {
            if (NumApps >= MaxApps)
                throw std::length_error("");

            new (&appBuffer[NumApps]) tMinExApplication(app, name);
            ++NumApps;
        }

        ...

        void runApps()
        {
            for (std::size_t i = 0; i < NumApps; ++i)
            {
                auto& app = Applications[i];
                // Frequency check
                // ...
                app.execute();
            }
        }

    private:
        typename std::aligned_storage<sizeof(tMinExApplication), alignof(tMinExApplication)>::type appBuffer[MaxApps];
        tMinExApplication* Applications;
        std::size_t NumApps = 0;
};
tMinExCoordinator<1> coordinator{};
...

上面链接的 std::aligned_storage 文档有一个示例 static_vector class 使用固定的内存缓冲区,如果在堆栈上构建向量,它将在堆栈上:

#include <iostream>
#include <type_traits>
#include <string>

template<class T, std::size_t N>
class static_vector
{
    // properly aligned uninitialized storage for N T's
    typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
    std::size_t m_size = 0;

public:
    // Create an object in aligned storage
    template<typename ...Args> void emplace_back(Args&&... args) 
    {
        if( m_size >= N ) // possible error handling
            throw std::bad_alloc{};

        // construct value in memory of aligned storage
        // using inplace operator new
        new(&data[m_size]) T(std::forward<Args>(args)...);
        ++m_size;
    }

    // Access an object in aligned storage
    const T& operator[](std::size_t pos) const 
    {
        // note: needs std::launder as of C++17
        return *reinterpret_cast<const T*>(&data[pos]);
    }

    // Delete objects from aligned storage
    ~static_vector() 
    {
        for(std::size_t pos = 0; pos < m_size; ++pos) {
            // note: needs std::launder as of C++17
            reinterpret_cast<T*>(&data[pos])->~T();
        }
    }
};

您可以在协调器中使用 class,并对其进行一些小的添加,以便它可以与循环一起使用,例如:

template<class T, std::size_t N>
class static_vector
{
    // properly aligned uninitialized storage for N T's
    typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
    std::size_t m_size = 0;

public:
    // Create an object in aligned storage
    template<typename ...Args> void emplace_back(Args&&... args) 
    {
        if( m_size >= N ) // possible error handling
            throw std::bad_alloc{};

        // construct value in memory of aligned storage
        // using inplace operator new
        new(&data[m_size]) T(std::forward<Args>(args)...);
        ++m_size;
    }

    // Access an object in aligned storage
    T& operator[](std::size_t pos)
    {
        // note: needs std::launder as of C++17
        return *reinterpret_cast<T*>(&data[pos]);
    }

    const T& operator[](std::size_t pos) const 
    {
        // note: needs std::launder as of C++17
        return *reinterpret_cast<const T*>(&data[pos]);
    }

    std::size_t size() const { return m_size; }
    std::size_t capacity() const { return N; }

    // iterator access to objects
    T* begin()
    {
        // note: needs std::launder as of C++17
        return reinterpret_cast<T*>(&data[0]);
    }
    T* end()
    {
        // note: needs std::launder as of C++17
        return reinterpret_cast<T*>(&data[m_size]);
    }
    const T* cbegin() const
    {
        // note: needs std::launder as of C++17
        return reinterpret_cast<const T*>(&data[0]);
    }
    const T* cend() const
    {
        // note: needs std::launder as of C++17
        return reinterpret_cast<const T*>(&data[m_size]);
    }

    // Delete objects from aligned storage
    ~static_vector() 
    {
        for(std::size_t pos = 0; pos < m_size; ++pos) {
            // note: needs std::launder as of C++17
            reinterpret_cast<T*>(&data[pos])->~T();
        }
    }
};

template<std::size_t MaxApps>
class tMinExCoordinator
{
    public:
        ...

        void addApp(iMinExApplication* app, const char name[]) 
        {
            Applications.emplace_back(app, name);
        }

        ...

        void runApps()
        {
            for (auto& app : Applications)
            {
                // Frequency check
                // ...
                app.execute();
            }
        }

    private:
        static_vector<tMinExApplication, MaxApps> Applications;
};

I have though of smart pointers, but am unsure if they use heap memory or not.

默认情况下,它们依赖于 newdelete,因此是动态内存。但是,您 可以 为它们提供指向堆栈内存的指针,如果您还为它们提供不会释放该内存的自定义删除器。