expose DBus interface property names as symbols

Enable a way to access the property names of a given DBus interface as a
constexpr symbol via the header [1]

4 use-cases:

- printing error / debug logs with the property name.
  e.g. '${property} missing from configuration'

- accessing DBus properties in applications which do not yet use the PDI
  generated bindings.

- preventing typos in DBus property names

- estimating the impact of removing a given DBus property from an
  interface. When using these symbols, it would cause a build failure in
  applications relying on the existence of that property.

Tested: a newly added unit test is using this feature.

References:
[1] https://discordapp.com/channels/775381525260664832/867820390406422538/1423317550732673206

Change-Id: I7b2906b6b1b445c3a49868ff1d50ced6ccaa5336
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/test/gen/test_property_names.cpp b/test/gen/test_property_names.cpp
new file mode 100644
index 0000000..e10ccf2
--- /dev/null
+++ b/test/gen/test_property_names.cpp
@@ -0,0 +1,32 @@
+#include "server/Test/common.hpp"
+#include "server/Test2/common.hpp"
+
+#include <print>
+
+#include <gtest/gtest.h>
+
+int main()
+{
+    // We can access the property name as a symbol.
+    // The property name is constexpr.
+
+    constexpr auto propName =
+        sdbusplus::common::server::Test::property_names::some_value;
+
+    // The property name can be used as part of error logs.
+
+    std::println("property {} not found \n", propName);
+
+    // If the property is removed from the interface definition, it will cause a
+    // build failure in applications still using that property. That can work
+    // even if the application is not (yet) using PDI-generated bindings for
+    // it's DBus interactions.
+
+    std::println("using property {} \n",
+                 sdbusplus::common::server::Test2::property_names::new_value);
+
+    EXPECT_EQ(sdbusplus::common::server::Test2::property_names::new_value,
+              "NewValue");
+
+    return 0;
+}
diff --git a/test/meson.build b/test/meson.build
index f67bf32..de7c0e4 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -123,6 +123,7 @@
     'test_aserver_no_uninitialized_value_constructor',
     'test_aserver_emit_interfaces_added_signal',
     'test_aserver_multiple_interfaces',
+    'test_property_names',
 ]
 
 foreach t : uninit_tests
@@ -133,7 +134,7 @@
             f'gen/@t@.cpp',
             generated_sources,
             include_directories: [root_inc],
-            dependencies: [sdbusplus_dep, server_test_dep],
+            dependencies: [sdbusplus_dep, server_test_dep, gtest_dep],
         ),
     )
 endforeach
diff --git a/tools/sdbusplus/templates/interface.common.hpp.mako b/tools/sdbusplus/templates/interface.common.hpp.mako
index a97a3c9..9d2a3b5 100644
--- a/tools/sdbusplus/templates/interface.common.hpp.mako
+++ b/tools/sdbusplus/templates/interface.common.hpp.mako
@@ -39,6 +39,13 @@
         % endfor
     };
 
+    struct property_names
+    {
+        % for p in interface.properties:
+        static constexpr auto ${p.snake_case} = "${p.name}";
+        % endfor
+    };
+
     using PropertiesVariant = sdbusplus::utility::dedup_variant_t<
         ${",\n        ".join(sorted(setOfPropertyTypes()))}>;
     % else: