BindMethod.hxx 8.75 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2016-2018 Max Kellermann <max.kellermann@gmail.com>
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
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef BIND_METHOD_HXX
#define BIND_METHOD_HXX

#include <type_traits>
#include <utility>

/**
 * This object stores a function pointer wrapping a method, and a
 * reference to an instance of the method's class.  It can be used to
 * wrap instance methods as callback functions.
 *
 * @param S the plain function signature type
 */
template<typename S=void()>
class BoundMethod;

Max Kellermann's avatar
Max Kellermann committed
46 47 48 49
template<typename R,
	bool NoExcept,
	typename... Args>
class BoundMethod<R(Args...) noexcept(NoExcept)> {
50
	typedef R (*function_pointer)(void *instance, Args... args) noexcept(NoExcept);
51 52 53 54 55

	void *instance_;
	function_pointer function;

public:
56 57 58
	/**
	 * Non-initializing trivial constructor
	 */
59 60 61
	BoundMethod() = default;

	constexpr
62
	BoundMethod(void *_instance, function_pointer _function) noexcept
63 64
		:instance_(_instance), function(_function) {}

65 66 67 68
	/**
	 * Construct an "undefined" object.  It must not be called,
	 * and its "bool" operator returns false.
	 */
69
	BoundMethod(std::nullptr_t) noexcept:function(nullptr) {}
70 71 72 73

	/**
	 * Was this object initialized with a valid function pointer?
	 */
74
	operator bool() const noexcept {
75 76 77
		return function != nullptr;
	}

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
	R operator()(Args... args) const {
		return function(instance_, std::forward<Args>(args)...);
	}
};

namespace BindMethodDetail {

/**
 * Helper class which converts a signature type to a method pointer
 * type.
 *
 * @param T the wrapped class
 * @param S the function signature type (plain, without instance
 * pointer)
 */
template<typename T, typename S>
struct MethodWithSignature;

Max Kellermann's avatar
Max Kellermann committed
96 97 98
template<typename T,
	 bool NoExcept,
	 typename R, typename... Args>
99 100
struct MethodWithSignature<T, R(Args...) noexcept(NoExcept)> {
	typedef R (T::*method_pointer)(Args...) noexcept(NoExcept);
101 102 103 104 105 106 107 108 109 110
};

/**
 * Helper class which introspects a method pointer type.
 *
 * @param M the method pointer type
 */
template<typename M>
struct MethodSignatureHelper;

111
template<typename R, bool NoExcept, typename T, typename... Args>
Max Kellermann's avatar
Max Kellermann committed
112
struct MethodSignatureHelper<R (T::*)(Args...) noexcept(NoExcept)> {
113 114 115 116 117 118 119 120 121
	/**
	 * The class which contains the given method (signature).
	 */
	typedef T class_type;

	/**
	 * A function type which describes the "plain" function
	 * signature.
	 */
122
	typedef R plain_signature(Args...) noexcept(NoExcept);
123 124 125 126 127 128 129 130 131
};

/**
 * Helper class which converts a plain function signature type to a
 * wrapper function pointer type.
 */
template<typename S>
struct MethodWrapperWithSignature;

132
template<typename R, bool NoExcept, typename... Args>
Max Kellermann's avatar
Max Kellermann committed
133
struct MethodWrapperWithSignature<R(Args...) noexcept(NoExcept)> {
134 135
	typedef R (*function_pointer)(void *instance,
				      Args...) noexcept(NoExcept);
136 137 138 139 140 141 142 143 144 145 146 147
};

/**
 * Generate a wrapper function.  Helper class for
 * #BindMethodWrapperGenerator.
 *
 * @param T the containing class
 * @param M the method pointer type
 * @param method the method pointer
 * @param R the return type
 * @param Args the method arguments
 */
Max Kellermann's avatar
Max Kellermann committed
148
template<typename T, bool NoExcept, typename M, M method, typename R, typename... Args>
149
struct BindMethodWrapperGenerator2 {
Max Kellermann's avatar
Max Kellermann committed
150
	static R Invoke(void *_instance, Args... args) noexcept(NoExcept) {
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
		auto &t = *(T *)_instance;
		return (t.*method)(std::forward<Args>(args)...);
	}
};

/**
 * Generate a wrapper function.
 *
 * @param T the containing class
 * @param M the method pointer type
 * @param method the method pointer
 * @param S the plain function signature type
 */
template<typename T, typename M, M method, typename S>
struct BindMethodWrapperGenerator;

167
template<typename T, bool NoExcept,
Max Kellermann's avatar
Max Kellermann committed
168 169 170
	 typename M, M method, typename R, typename... Args>
struct BindMethodWrapperGenerator<T, M, method, R(Args...) noexcept(NoExcept)>
	: BindMethodWrapperGenerator2<T, NoExcept, M, method, R, Args...> {
171 172 173 174 175
};

template<typename T, typename S,
	 typename MethodWithSignature<T, S>::method_pointer method>
typename MethodWrapperWithSignature<S>::function_pointer
176
MakeBindMethodWrapper() noexcept
177 178 179 180
{
	return BindMethodWrapperGenerator<T, typename MethodWithSignature<T, S>::method_pointer, method, S>::Invoke;
}

181 182 183 184 185 186 187 188
/**
 * Helper class which introspects a function pointer type.
 *
 * @param S the function type
 */
template<typename S>
struct FunctionTraits;

189
template<typename R, bool NoExcept, typename... Args>
Max Kellermann's avatar
Max Kellermann committed
190
struct FunctionTraits<R(Args...) noexcept(NoExcept)> {
191 192 193 194
	/**
	 * A function type which describes the "plain" function
	 * signature.
	 */
195
	typedef R function_type(Args...) noexcept(NoExcept);
196 197 198 199 200

	/**
	 * A function pointer type which describes the "plain"
	 * function signature.
	 */
201
	typedef R (*pointer)(Args...) noexcept(NoExcept);
202 203 204 205 206 207 208 209 210 211 212 213
};

/**
 * Generate a wrapper function for a plain function which ignores the
 * instance pointer.  Helper class for
 * #BindFunctionWrapperGenerator.
 *
 * @param F the function pointer type
 * @param function the function pointer
 * @param R the return type
 * @param Args the function arguments
 */
Max Kellermann's avatar
Max Kellermann committed
214
template<bool NoExcept, typename F, F function, typename R, typename... Args>
215
struct BindFunctionWrapperGenerator2 {
Max Kellermann's avatar
Max Kellermann committed
216
	static R Invoke(void *, Args... args) noexcept(NoExcept) {
217 218 219 220 221 222 223 224 225 226 227 228 229 230
		return function(std::forward<Args>(args)...);
	}
};

/**
 * Generate a wrapper function.
 *
 * @param S the plain function signature type
 * @param P the plain function pointer type
 * @param function the function pointer
 */
template<typename S, typename P, P function>
struct BindFunctionWrapperGenerator;

231
template<typename P, P function, bool NoExcept, typename R, typename... Args>
Max Kellermann's avatar
Max Kellermann committed
232 233
struct BindFunctionWrapperGenerator<R(Args...) noexcept(NoExcept), P, function>
	: BindFunctionWrapperGenerator2<NoExcept, P, function, R, Args...> {
234 235
};

236
template<typename T, typename T::pointer function>
237
typename MethodWrapperWithSignature<typename T::function_type>::function_pointer
238
MakeBindFunctionWrapper() noexcept
239 240
{
	return BindFunctionWrapperGenerator<typename T::function_type,
241
					    typename T::pointer,
242 243 244
					    function>::Invoke;
}

245 246 247 248 249 250 251 252 253 254 255 256 257
} /* namespace BindMethodDetail */

/**
 * Construct a #BoundMethod instance.
 *
 * @param T the containing class
 * @param S the plain function signature type
 * @param method the method pointer
 * @param instance the instance of #T to be bound
 */
template<typename T, typename S,
	 typename BindMethodDetail::MethodWithSignature<T, S>::method_pointer method>
constexpr BoundMethod<S>
258
BindMethod(T &_instance) noexcept
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
{
	return BoundMethod<S>(&_instance,
			      BindMethodDetail::MakeBindMethodWrapper<T, S, method>());
}

/**
 * Shortcut macro which takes an instance and a method pointer and
 * constructs a #BoundMethod instance.
 */
#define BIND_METHOD(instance, method) \
	BindMethod<typename BindMethodDetail::MethodSignatureHelper<decltype(method)>::class_type, \
		   typename BindMethodDetail::MethodSignatureHelper<decltype(method)>::plain_signature, \
		   method>(instance)

/**
 * Shortcut wrapper for BIND_METHOD() which assumes "*this" is the
 * instance to be bound.
 */
#define BIND_THIS_METHOD(method) BIND_METHOD(*this, &std::remove_reference<decltype(*this)>::type::method)

279 280 281 282 283 284
/**
 * Construct a #BoundMethod instance for a plain function.
 *
 * @param T the #FunctionTraits class
 * @param function the function pointer
 */
285
template<typename T, typename T::pointer function>
286
constexpr BoundMethod<typename T::function_type>
287
BindFunction() noexcept
288 289 290 291 292 293 294 295 296 297 298 299
{
	return BoundMethod<typename T::function_type>(nullptr,
						      BindMethodDetail::MakeBindFunctionWrapper<T, function>());
}

/**
 * Shortcut macro which takes a function pointer and constructs a
 * #BoundMethod instance.
 */
#define BIND_FUNCTION(function) \
	BindFunction<typename BindMethodDetail::FunctionTraits<decltype(function)>, &function>()

300
#endif