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)?
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();
}
}