Search code examples
c++polymorphismreal-time

Return polymorphic object in real-time critical code


Is it possible in C++ to have a function return a polymorphic object in a way that is real-time safe?

For context: I want to implement a function that returns information about errors detected in a system. My first naive approach was to implement it like this (simplified):

class Error { ... };  // abstract base class
// different error cases:
class FooError: public Error { public: FooError(int foo) {...}};
class BarError: public Error { public: BarError(int bar, float baz) {...}};

// check if there is an error and return the first one detected
std::shared_ptr<Error> get_error() {
    if (is_error_foo()) {
        return std::make_shared<FooError>(42);
    }
    if (is_error_bar()) {
        return std::make_shared<BarError>(13, 3.14);
    }

    ...
}

Now the problem is that the code is for a real-time critical system and hence should not use dynamic memory allocation. Therefore I should not use make_shared. Is there an alternative way to implement the above that does not use dynamic memory allocation (or other things that should not be done in real-time code)?


Solution

  • A base class making message collection high performance can be done as:

    #include <array>
    #include <vector>
    #include <utility>
    #include <iostream>
    
    using MessageParam = std::variant<int, float>;
    
    template<typename T>
    concept MessageType = std::is_constructible_v<MessageParam, T>;
    
    template<int MAX_PARAM>
    class Message
    {
    public:
        template<MessageType ... Args>
        Message(std::string_view format, Args... args)
            requires (sizeof...(args) <= MAX_PARAM) :
            m_format(format),
            m_nr_params(sizeof...(args)),
            m_params{ args... }
        {}
    
        auto msg() const
        {
            // Some formatting, this is just an example:
            assert(m_format.data() + m_format.size() == '\0' && "Messages require null terminated strings to use boost format");
            boost::format fmt{ m_format.data() }; 
            for (size_t i = 0; i < m_nr_params; ++i)
            {
                if (std::holds_alternative<float>(m_params[i]))
                {
                    fmt% std::get<float>(m_params[i]);
                }
                if (std::holds_alternative<int>(m_params[i]))
                {
                    fmt% std::get<int>(m_params[i]);
                }
            }
            return fmt.str();
        }
    
    private:
        std::string_view m_format;
        size_t m_nr_params;
        std::array<MessageParam, MAX_PARAM> m_params;
    };
    
    using MyMessageBase = Message<3>;
    
    class NumberTooBigMessage : public MyMessageBase
    {
    public:
        explicit NumberTooBigMessage(int number) : 
            MyMessageBase("Number is too big: %1%", number) 
        {}
    };
    
    auto alternative_number_to_big_message(int number)
    {
        return MyMessageBase("Number is too big: %1%", number);
    }
    
    void test()
    {
        std::vector<MyMessageBase> messages;
        messages.push_back(NumberTooBigMessage(1));
        messages.push_back(alternative_number_to_big_message(1));
    
        for (auto& message : messages)
        {
            std::cout << message.msg();
        }
    }