summaryrefslogtreecommitdiff
path: root/zend/symbol.h
blob: 6a470bd20fa63dda647618c65a5cf75ec5d6d430 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/**
 *  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(DL_FETCH_SYMBOL((DL_HANDLE)handle, name)) {}

    /**
     *  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
 */
}