summaryrefslogtreecommitdiff
path: root/zend/symbol.h
diff options
context:
space:
mode:
Diffstat (limited to 'zend/symbol.h')
-rw-r--r--zend/symbol.h160
1 files changed, 160 insertions, 0 deletions
diff --git a/zend/symbol.h b/zend/symbol.h
new file mode 100644
index 0000000..66488dc
--- /dev/null
+++ b/zend/symbol.h
@@ -0,0 +1,160 @@
+/**
+ * Symbol.h
+ *
+ * C++ utility class that wraps around a dlsym() call, and that allows us
+ * to do dlsym() calls with template parameters instead of ugly casts that
+ * are necessary in C to turn a void* into a function pointer
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2015 Copernica BV
+ */
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Make the Symbol class a templated class
+ */
+template <class T> class Symbol {};
+
+/**
+ * So that we can write a partial specialisation
+ * using a function prototype, indicating the
+ * prototype usable for the given callback
+ */
+template <class T, class ...Arguments>
+class Symbol<T(Arguments...)>
+{
+private:
+ /**
+ * Store pointer to the actual dynamically loaded
+ * function. This is done in a union because the
+ * result from a dlsym call is a void pointer which
+ * cannot be legally cast into a function pointer.
+ *
+ * We therefore always set the first type (which is
+ * void* making it legal) and, because all members
+ * in a union share the same address, we can then
+ * read the second type and actually call it.
+ *
+ * @var Callable
+ */
+ union Callable {
+
+ /**
+ * Property for getting and setting the return
+ * value from dlsym. This is always a void*
+ * @var void
+ */
+ void *ptr;
+
+ /**
+ * Property for executing the mapped function
+ *
+ * @param mixed,... function parameters
+ * @return mixed
+ *
+ * @var function
+ */
+ T (*func)(Arguments...);
+
+
+ /**
+ * Constructor
+ */
+ Callable() : ptr(nullptr) {}
+
+ /**
+ * We may be moved
+ *
+ * @param callable the callable we are moving
+ */
+ Callable(Callable&& callable) :
+ ptr(callable.ptr)
+ {
+ // the other callable no longer has a handle
+ callable.ptr = nullptr;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param function the mapped function
+ */
+ Callable(void *function) : ptr(function) {}
+
+ } _method;
+
+public:
+ /**
+ * Constructor
+ */
+ Symbol() = default;
+
+ /**
+ * Constructor
+ * @param handle The library handle to load the function from
+ * @param name Name of the function
+ */
+ Symbol(void *handle, const char *name) :
+ _method(dlsym(handle, name)) {}
+
+ /**
+ * Move constructor
+ * @param other
+ */
+ Symbol(Symbol &&other) = default;
+
+ /**
+ * Destructor
+ */
+ virtual ~Symbol() {}
+
+ /**
+ * Is this a valid function or not?
+ * @return bool
+ */
+ bool valid() const
+ {
+ return _method.ptr != nullptr;
+ }
+
+ /**
+ * The library object can also be used as in a boolean context,
+ * for that there is an implementation of a casting operator, and
+ * the negate operator
+ * @return bool
+ */
+ operator bool () const { return valid(); }
+ bool operator ! () const { return !valid(); }
+
+ /**
+ * Test whether we are a valid object
+ * @param nullptr test if we are null
+ */
+ bool operator==(std::nullptr_t /* nullptr */) const { return !valid(); }
+ bool operator!=(std::nullptr_t /* nullptr */) const { return valid(); }
+
+ /**
+ * Invoke the function
+ *
+ * @param mixed,...
+ * @throws std::bad_function_call
+ */
+ T operator()(Arguments... parameters) const
+ {
+ // check whether we have a valid function
+ if (_method.ptr == nullptr) throw std::bad_function_call();
+
+ // execute the method given all the parameters
+ return _method.func(std::forward<Arguments>(parameters)...);
+ }
+};
+
+/**
+ * End of namespace
+ */
+}
+