sdbus++: events: unpack an event from JSON
Generate code necessary to unpack a generated event from its
JSON representation.
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I7a28617f3f0374968609328a44587b655a9141c1
diff --git a/include/sdbusplus/exception.hpp b/include/sdbusplus/exception.hpp
index 80f0d22..88055f2 100644
--- a/include/sdbusplus/exception.hpp
+++ b/include/sdbusplus/exception.hpp
@@ -7,6 +7,7 @@
#include <sdbusplus/utility/consteval_string.hpp>
#include <exception>
+#include <source_location>
#include <string>
namespace sdbusplus
@@ -198,6 +199,18 @@
int get_errno() const noexcept override;
};
+/** Throw a generated_event from the JSON representation.
+ *
+ * @param[in] j - JSON representation of the event.
+ * @param[in] source - The source code location of the origin.
+ */
+void throw_via_json(
+ const nlohmann::json& j,
+ const std::source_location& source = std::source_location::current());
+
+/** Get the list of known events by name. */
+auto known_events() -> std::vector<std::string>;
+
} // namespace exception
using exception_t = exception::exception;
diff --git a/include/sdbusplus/sdbuspp_support/event.hpp b/include/sdbusplus/sdbuspp_support/event.hpp
new file mode 100644
index 0000000..eea8947
--- /dev/null
+++ b/include/sdbusplus/sdbuspp_support/event.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <nlohmann/json.hpp>
+#include <sdbusplus/exception.hpp>
+
+#include <source_location>
+
+namespace sdbusplus::sdbuspp
+{
+
+using register_hook =
+ std::function<void(const nlohmann::json&, const std::source_location&)>;
+
+void register_event(const std::string&, register_hook);
+
+template <typename T>
+struct register_event_helper
+{
+ static void hook()
+ {
+ register_event(T::errName, throw_event);
+ }
+
+ static void throw_event(const nlohmann::json& j,
+ const std::source_location& location)
+ {
+ throw T(j, location);
+ }
+};
+} // namespace sdbusplus::sdbuspp
diff --git a/src/exception.cpp b/src/exception.cpp
index 565e059..cd09c0c 100644
--- a/src/exception.cpp
+++ b/src/exception.cpp
@@ -1,4 +1,5 @@
#include <sdbusplus/exception.hpp>
+#include <sdbusplus/sdbuspp_support/event.hpp>
#include <cerrno>
#include <stdexcept>
@@ -9,9 +10,7 @@
#pragma clang diagnostic ignored "-Wc99-extensions"
#endif
-namespace sdbusplus
-{
-namespace exception
+namespace sdbusplus::exception
{
void exception::unused() const noexcept {}
@@ -212,8 +211,45 @@
return ECANCELED;
}
-} // namespace exception
-} // namespace sdbusplus
+static std::unordered_map<std::string, sdbusplus::sdbuspp::register_hook>
+ event_hooks = {};
+
+void throw_via_json(const nlohmann::json& j, const std::source_location& source)
+{
+ for (const auto& i : j.items())
+ {
+ if (auto it = event_hooks.find(i.key()); it != event_hooks.end())
+ {
+ it->second(j, source);
+ }
+ }
+}
+
+auto known_events() -> std::vector<std::string>
+{
+ std::vector<std::string> result{};
+
+ for (const auto& [key, _] : event_hooks)
+ {
+ result.emplace_back(key);
+ }
+
+ std::ranges::sort(result);
+
+ return result;
+}
+
+} // namespace sdbusplus::exception
+
+namespace sdbusplus::sdbuspp
+{
+
+void register_event(const std::string& event, register_hook throw_hook)
+{
+ sdbusplus::exception::event_hooks.emplace(event, throw_hook);
+}
+
+} // namespace sdbusplus::sdbuspp
#ifdef __clang__
#pragma clang diagnostic pop
diff --git a/tools/sdbusplus/templates/event.cpp.mako b/tools/sdbusplus/templates/event.cpp.mako
index 7613e85..1473f21 100644
--- a/tools/sdbusplus/templates/event.cpp.mako
+++ b/tools/sdbusplus/templates/event.cpp.mako
@@ -22,17 +22,68 @@
{
nlohmann::json j = { };
% for m in event.metadata:
+ % if m.typeName == "object_path":
+ j["${m.SNAKE_CASE}"] = ${m.camelCase}.str;
+ % elif m.is_enum():
+ j["${m.SNAKE_CASE}"] = sdbusplus::message::convert_to_string(${m.camelCase});
+ % else:
j["${m.SNAKE_CASE}"] = ${m.camelCase};
+ % endif
% endfor
// Add common source and pid info.
nlohmann::json source_info = {};
- source_info["FILE"] = source.file_name();
- source_info["LINE"] = source.line();
- source_info["COLUMN"] = source.column();
- source_info["FUNCTION"] = source.function_name();
+ source_info["FILE"] = source_file;
+ source_info["FUNCTION"] = source_func;
+ source_info["LINE"] = source_line;
+ source_info["COLUMN"] = source_column;
source_info["PID"] = pid;
j["_SOURCE"] = source_info;
return nlohmann::json{ { errName, std::move(j) } };
}
+
+${event.CamelCase}::${event.CamelCase}(
+ const nlohmann::json& j, const std::source_location& s)
+{
+ const nlohmann::json& self = j.at(errName);
+
+% for m in event.metadata:
+ % if m.typeName == "object_path":
+ ${m.camelCase} = self.at("${m.SNAKE_CASE}").get<std::string>();
+ % elif m.is_enum():
+ ${m.camelCase} =
+ sdbusplus::message::convert_from_string<decltype(${m.camelCase})>(
+ self.at("${m.SNAKE_CASE}")
+ ).value();
+ % else:
+ ${m.camelCase} = self.at("${m.SNAKE_CASE}");
+ % endif
+% endfor
+
+ if (!self.contains("_SOURCE"))
+ {
+ source_file = s.file_name();
+ source_func = s.function_name();
+ source_line = s.line();
+ source_column = s.column();
+ pid = getpid();
+ }
+ else
+ {
+ source_file = self.at("FILE");
+ source_func = self.at("FUNCTION");
+ source_line = self.at("LINE");
+ source_column = self.at("COLUMN");
+ pid = self.at("PID");
+ }
+
+}
+
+namespace details
+{
+void register_${event.CamelCase}()
+{
+ sdbusplus::sdbuspp::register_event_helper<${event.CamelCase}>::hook();
+}
+}
diff --git a/tools/sdbusplus/templates/event.hpp.mako b/tools/sdbusplus/templates/event.hpp.mako
index 382f67c..8cf6e31 100644
--- a/tools/sdbusplus/templates/event.hpp.mako
+++ b/tools/sdbusplus/templates/event.hpp.mako
@@ -1,24 +1,29 @@
struct ${event.CamelCase} final :
public sdbusplus::exception::generated_event<${event.CamelCase}>
{
+ ${event.CamelCase}(const nlohmann::json&, const std::source_location&);
%if len(event.metadata) == 0:
${event.CamelCase}(
- std::source_location source = std::source_location::current()) :
- source(source), pid(getpid())
+ const std::source_location& source = std::source_location::current()) :
+ source_file(source.file_name()), source_func(source.function_name()),
+ source_line(source.line()), source_column(source.column()),
+ pid(getpid())
{}
%else:
${event.CamelCase}(
${", ".join([
f"metadata_t<\"{m.SNAKE_CASE}\">, {m.cppTypeParam(events.name)} {m.camelCase}_"
for m in event.metadata ])},
- std::source_location source = std::source_location::current()) :
+ const std::source_location& source = std::source_location::current()) :
${", ".join([
f"{m.camelCase}({m.camelCase}_)" for m in event.metadata ])},
- source(source), pid(getpid())
+ source_file(source.file_name()), source_func(source.function_name()),
+ source_line(source.line()), source_column(source.column()),
+ pid(getpid())
{}
%for m in event.metadata:
- const ${m.cppTypeParam(events.name)} ${m.camelCase};
+ ${m.cppTypeParam(events.name)} ${m.camelCase};
%endfor
%endif
@@ -26,7 +31,10 @@
int set_error(SdBusInterface*, sd_bus_error*) const override;
auto to_json() const -> nlohmann::json override;
- std::source_location source;
+ std::string source_file;
+ std::string source_func;
+ size_t source_line;
+ size_t source_column;
pid_t pid;
static constexpr auto errName =
@@ -39,3 +47,12 @@
static constexpr auto errErrno = ${event.errno};
};
+
+namespace details
+{
+void register_${event.CamelCase}();
+[[gnu::constructor]] inline void force_register_${event.CamelCase}()
+{
+ register_${event.CamelCase}();
+}
+} // namespace details
diff --git a/tools/sdbusplus/templates/events.cpp.mako b/tools/sdbusplus/templates/events.cpp.mako
index 40105be..44290fb 100644
--- a/tools/sdbusplus/templates/events.cpp.mako
+++ b/tools/sdbusplus/templates/events.cpp.mako
@@ -1,6 +1,7 @@
-#include <${events.headerFile("event")}>
#include <nlohmann/json.hpp>
-#
+#include <sdbusplus/sdbuspp_support/event.hpp>
+#include <${events.headerFile("event")}>
+#include <iostream>
%if events.errors:
namespace sdbusplus::error::${events.cppNamespacedClass()}