source/base: Make callback handling generic
diff --git a/src/sdeventplus/source/base.cpp b/src/sdeventplus/source/base.cpp
index 32e6edf..d63a03e 100644
--- a/src/sdeventplus/source/base.cpp
+++ b/src/sdeventplus/source/base.cpp
@@ -1,9 +1,6 @@
-#include <cerrno>
-#include <cstdio>
 #include <functional>
 #include <sdeventplus/exception.hpp>
 #include <sdeventplus/internal/sdevent.hpp>
-#include <sdeventplus/internal/utils.hpp>
 #include <sdeventplus/source/base.hpp>
 #include <type_traits>
 #include <utility>
@@ -53,22 +50,10 @@
     }
 }
 
-static int prepare_callback(sd_event_source*, void* userdata)
-{
-    if (userdata == nullptr)
-    {
-        fprintf(stderr, "sdeventplus: prepare_callback: Missing userdata\n");
-        return -EINVAL;
-    }
-    Base* base = reinterpret_cast<Base*>(userdata);
-    return internal::performCallback("prepare_callback", base->get_prepare(),
-                                     std::ref(*base));
-}
-
 void Base::set_prepare(Callback&& callback)
 {
     int r = event.getSdEvent()->sd_event_source_set_prepare(
-        source.get(), callback ? prepare_callback : nullptr);
+        source.get(), callback ? prepareCallback : nullptr);
     if (r < 0)
     {
         prepare = nullptr;
@@ -179,6 +164,12 @@
     return *this;
 }
 
+int Base::prepareCallback(sd_event_source* source, void* userdata)
+{
+    return sourceCallback<Callback, Base, &Base::get_prepare>("prepareCallback",
+                                                              source, userdata);
+}
+
 void Base::set_userdata()
 {
     event.getSdEvent()->sd_event_source_set_userdata(source.get(), this);
diff --git a/src/sdeventplus/source/base.hpp b/src/sdeventplus/source/base.hpp
index 9253b71..d37102b 100644
--- a/src/sdeventplus/source/base.hpp
+++ b/src/sdeventplus/source/base.hpp
@@ -1,9 +1,12 @@
 #pragma once
 
+#include <cerrno>
 #include <cstdint>
+#include <cstdio>
 #include <functional>
 #include <sdeventplus/event.hpp>
 #include <sdeventplus/internal/sdref.hpp>
+#include <sdeventplus/internal/utils.hpp>
 #include <systemd/sd-bus.h>
 #include <type_traits>
 
@@ -25,7 +28,6 @@
     const char* get_description() const;
     void set_description(const char* description) const;
     void set_prepare(Callback&& callback);
-    const Callback& get_prepare() const;
     int get_pending() const;
     int64_t get_priority() const;
     void set_priority(int64_t priority) const;
@@ -48,10 +50,28 @@
     Base(Base&& other);
     Base& operator=(Base&& other);
 
+    const Callback& get_prepare() const;
+
+    template <typename Callback, class Source,
+              const Callback& (Source::*getter)() const, typename... Args>
+    static int sourceCallback(const char* name, sd_event_source*,
+                              void* userdata, Args... args)
+    {
+        if (userdata == nullptr)
+        {
+            fprintf(stderr, "sdeventplus: %s: Missing userdata\n", name);
+            return -EINVAL;
+        }
+        Source* source = reinterpret_cast<Source*>(userdata);
+        return internal::performCallback(name, (source->*getter)(),
+                                         std::ref(*source), args...);
+    }
+
   private:
     Callback prepare;
 
     void set_userdata();
+    static int prepareCallback(sd_event_source* source, void* userdata);
 };
 
 } // namespace source
diff --git a/test/source/base.cpp b/test/source/base.cpp
index 435b6f2..bc0eec0 100644
--- a/test/source/base.cpp
+++ b/test/source/base.cpp
@@ -36,6 +36,8 @@
         Base(event, source, std::false_type())
     {
     }
+
+    using Base::get_prepare;
 };
 
 class BaseTest : public testing::Test
@@ -76,7 +78,7 @@
         return ret;
     }
 
-    void set_prepare_placeholder(Base& base)
+    void set_prepare_placeholder(BaseImpl& base)
     {
         EXPECT_CALL(mock, sd_event_source_set_prepare(base.get(), testing::_))
             .WillOnce(Return(0));