BindMethod.hxx 9.35 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
 *
 * 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

Max Kellermann's avatar
Max Kellermann committed
33 34
#include "Compiler.h"

35 36 37 38 39 40 41 42 43 44 45 46 47
#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
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
#if GCC_OLDER_THAN(7,0)
static constexpr bool NoExcept = false;
#endif

template<typename R,
#if !GCC_OLDER_THAN(7,0)
	bool NoExcept,
#endif
	typename... Args>
class BoundMethod<R(Args...) noexcept(NoExcept)> {
	typedef R (*function_pointer)(void *instance, Args... args)
#if !GCC_OLDER_THAN(7,0)
		noexcept(NoExcept)
#endif
		;
63 64 65 66 67

	void *instance_;
	function_pointer function;

public:
68 69 70
	/**
	 * Non-initializing trivial constructor
	 */
71 72 73
	BoundMethod() = default;

	constexpr
74
	BoundMethod(void *_instance, function_pointer _function) noexcept
75 76
		:instance_(_instance), function(_function) {}

77 78 79 80
	/**
	 * Construct an "undefined" object.  It must not be called,
	 * and its "bool" operator returns false.
	 */
81
	BoundMethod(std::nullptr_t) noexcept:function(nullptr) {}
82 83 84 85

	/**
	 * Was this object initialized with a valid function pointer?
	 */
86
	operator bool() const noexcept {
87 88 89
		return function != nullptr;
	}

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
	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
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
template<typename T,
#if !GCC_OLDER_THAN(7,0)
	 bool NoExcept,
#endif
	 typename R, typename... Args>
struct MethodWithSignature<T, R(Args...)
#if !GCC_OLDER_THAN(7,0)
			   noexcept(NoExcept)
#endif
			   > {
	typedef R (T::*method_pointer)(Args...)
#if !GCC_OLDER_THAN(7,0)
		noexcept(NoExcept)
#endif
		;
123 124 125 126 127 128 129 130 131 132
};

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

Max Kellermann's avatar
Max Kellermann committed
133 134 135 136 137 138
template<typename R,
#if !GCC_OLDER_THAN(7,0)
	 bool NoExcept,
#endif
	 typename T, typename... Args>
struct MethodSignatureHelper<R (T::*)(Args...) noexcept(NoExcept)> {
139 140 141 142 143 144 145 146 147
	/**
	 * The class which contains the given method (signature).
	 */
	typedef T class_type;

	/**
	 * A function type which describes the "plain" function
	 * signature.
	 */
Max Kellermann's avatar
Max Kellermann committed
148 149 150 151 152
	typedef R plain_signature(Args...)
#if !GCC_OLDER_THAN(7,0)
		noexcept(NoExcept)
#endif
		;
153 154 155 156 157 158 159 160 161
};

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

Max Kellermann's avatar
Max Kellermann committed
162 163 164 165 166 167 168 169 170 171 172
template<typename R,
#if !GCC_OLDER_THAN(7,0)
	 bool NoExcept,
#endif
	 typename... Args>
struct MethodWrapperWithSignature<R(Args...) noexcept(NoExcept)> {
	typedef R (*function_pointer)(void *instance, Args...)
#if !GCC_OLDER_THAN(7,0)
		noexcept(NoExcept)
#endif
		;
173 174 175 176 177 178 179 180 181 182 183 184
};

/**
 * 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
185
template<typename T, bool NoExcept, typename M, M method, typename R, typename... Args>
186
struct BindMethodWrapperGenerator2 {
Max Kellermann's avatar
Max Kellermann committed
187
	static R Invoke(void *_instance, Args... args) noexcept(NoExcept) {
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
		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;

Max Kellermann's avatar
Max Kellermann committed
204 205 206 207 208 209 210
template<typename T,
#if !GCC_OLDER_THAN(7,0)
	 bool NoExcept,
#endif
	 typename M, M method, typename R, typename... Args>
struct BindMethodWrapperGenerator<T, M, method, R(Args...) noexcept(NoExcept)>
	: BindMethodWrapperGenerator2<T, NoExcept, M, method, R, Args...> {
211 212 213 214 215
};

template<typename T, typename S,
	 typename MethodWithSignature<T, S>::method_pointer method>
typename MethodWrapperWithSignature<S>::function_pointer
216
MakeBindMethodWrapper() noexcept
217 218 219 220
{
	return BindMethodWrapperGenerator<T, typename MethodWithSignature<T, S>::method_pointer, method, S>::Invoke;
}

221 222 223 224 225 226 227 228
/**
 * Helper class which introspects a function pointer type.
 *
 * @param S the function type
 */
template<typename S>
struct FunctionTraits;

Max Kellermann's avatar
Max Kellermann committed
229 230 231 232 233 234
template<typename R,
#if !GCC_OLDER_THAN(7,0)
	 bool NoExcept,
#endif
	 typename... Args>
struct FunctionTraits<R(Args...) noexcept(NoExcept)> {
235 236 237 238
	/**
	 * A function type which describes the "plain" function
	 * signature.
	 */
Max Kellermann's avatar
Max Kellermann committed
239 240 241 242 243
	typedef R function_type(Args...)
#if !GCC_OLDER_THAN(7,0)
		noexcept(NoExcept)
#endif
		;
244 245 246 247 248

	/**
	 * A function pointer type which describes the "plain"
	 * function signature.
	 */
Max Kellermann's avatar
Max Kellermann committed
249 250 251 252 253
	typedef R (*pointer_type)(Args...)
#if !GCC_OLDER_THAN(7,0)
		noexcept(NoExcept)
#endif
		;
254 255 256 257 258 259 260 261 262 263 264 265
};

/**
 * 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
266
template<bool NoExcept, typename F, F function, typename R, typename... Args>
267
struct BindFunctionWrapperGenerator2 {
Max Kellermann's avatar
Max Kellermann committed
268
	static R Invoke(void *, Args... args) noexcept(NoExcept) {
269 270 271 272 273 274 275 276 277 278 279 280 281 282
		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;

Max Kellermann's avatar
Max Kellermann committed
283 284 285 286 287 288 289
template<typename P, P function,
#if !GCC_OLDER_THAN(7,0)
	 bool NoExcept,
#endif
	 typename R, typename... Args>
struct BindFunctionWrapperGenerator<R(Args...) noexcept(NoExcept), P, function>
	: BindFunctionWrapperGenerator2<NoExcept, P, function, R, Args...> {
290 291 292 293
};

template<typename T, typename T::pointer_type function>
typename MethodWrapperWithSignature<typename T::function_type>::function_pointer
294
MakeBindFunctionWrapper() noexcept
295 296 297 298 299 300
{
	return BindFunctionWrapperGenerator<typename T::function_type,
					    typename T::pointer_type,
					    function>::Invoke;
}

301 302 303 304 305 306 307 308 309 310 311 312 313
} /* 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>
314
BindMethod(T &_instance) noexcept
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
{
	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)

335 336 337 338 339 340 341 342
/**
 * Construct a #BoundMethod instance for a plain function.
 *
 * @param T the #FunctionTraits class
 * @param function the function pointer
 */
template<typename T, typename T::pointer_type function>
constexpr BoundMethod<typename T::function_type>
343
BindFunction() noexcept
344 345 346 347 348 349 350 351 352 353 354 355
{
	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>()

356
#endif